diff --git a/src/cs/bfast/Vim.BFast.Tests/BFastTestProgram.cs b/src/cs/bfast/Vim.BFast.Tests/BFastTestProgram.cs deleted file mode 100644 index f4b3cab8..00000000 --- a/src/cs/bfast/Vim.BFast.Tests/BFastTestProgram.cs +++ /dev/null @@ -1,198 +0,0 @@ -/* - BFAST - Binary Format for Array Streaming and Transmission - Copyright 2019, VIMaec LLC - Copyright 2018, Ara 3D, Inc. - Usage licensed under terms of MIT License - https://github.com/vimaec/bfast -*/ - -using NUnit.Framework; -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Numerics; - -namespace Vim.BFast.Tests -{ - public static class BFastTests - { - public static int Mb = 1000 * 1000; - public static int Gb = 1000 * Mb; - - static byte[] ByteArray(int numBytes) => - Enumerable.Range(0, numBytes).Select(i => (byte)i).ToArray(); - - static readonly byte[] Array1MB - = ByteArray(Mb); - - static readonly double[] Array1GB - = Enumerable.Range(0, Gb / 8).Select(i => (double)i).ToArray(); - - public static (string, byte[])[] ZeroBuffers - = Enumerable.Empty<(string, byte[])>().ToArray(); - - public static (string, byte[])[] Ten1MBBuffers - = Enumerable.Range(0, 10).Select(i => (i.ToString(), Array1MB)).ToArray(); - - public static (string, double[])[] One1GBBuffer - => Enumerable.Range(0, 1).Select(i => (i.ToString(), Array1GB)).ToArray(); - - public static void TestBFastBytes(byte[] bytes) - { - Console.WriteLine($"Size of buffer = {bytes.Length}"); - Console.WriteLine($"First 8 bytes = {string.Join(", ", bytes.Take(8))}"); - } - - public class DisposableTimer : IDisposable - { - Stopwatch Stopwatch = Stopwatch.StartNew(); - - public void Dispose() - => Console.WriteLine($"Elapsed = {Stopwatch.ElapsedMilliseconds / 1000}s {Stopwatch.ElapsedMilliseconds % 1000}ms"); - } - - public static DisposableTimer CreateTimer(string message = null) - { - Console.WriteLine($"Starting timer {message ?? string.Empty}"); - return new DisposableTimer(); - } - - [Test] - public static void TestStringPacking() - { - var noStrings = new string[0]; - var oneStrings = new[] { "" }; - var twoStrings = new[] { "", "ab" }; - var threeStrings = new[] { "a", "b", "" }; - var noPacked = BFast.PackStrings(noStrings); - var onePacked = BFast.PackStrings(oneStrings); - var twoPacked = BFast.PackStrings(twoStrings); - var threePacked = BFast.PackStrings(threeStrings); - Assert.AreEqual(0, noPacked.Length); - Assert.AreEqual(1, onePacked.Length); - Assert.AreEqual(4, twoPacked.Length); - Assert.AreEqual(5, threePacked.Length); - Assert.AreEqual(noStrings, BFast.UnpackStrings(noPacked)); - Assert.AreEqual(oneStrings, BFast.UnpackStrings(onePacked)); - Assert.AreEqual(twoStrings, BFast.UnpackStrings(twoPacked)); - Assert.AreEqual(threeStrings, BFast.UnpackStrings(threePacked)); - } - - [Test] - public static void BasicTests() - { - using (CreateTimer("ZeroBuffers")) - { - var bytes = BFast.WriteBFastToBytes(ZeroBuffers); - TestBFastBytes(bytes); - var tmp = BFast.ReadBFast(bytes).ToArray(); - Assert.AreEqual(0, tmp.Length); - } - using (CreateTimer("Ten1MBBuffers")) - { - var bytes = BFast.WriteBFastToBytes(Ten1MBBuffers); - TestBFastBytes(bytes); - var tmp = BFast.ReadBFast(bytes).ToArray(); - Assert.AreEqual(10, tmp.Length); - Assert.AreEqual(tmp.Select(x => x.Name).ToArray(), Enumerable.Range(0, 10).Select(x => x.ToString()).ToArray()); - Assert.AreEqual(tmp.Select(x => (int)x.NumBytes()).ToArray(), Enumerable.Repeat(Mb, 10).ToArray()); - - for (var i = 0; i < 10; ++i) - Assert.AreEqual(Ten1MBBuffers[i].Item2, tmp[i].ToBytes(), $"Buffer {i} are different"); - } - using (CreateTimer("OneGBBuffer")) - { - //Enumerable.Range(0, Gb).Select(i => (double)i).ToArray() - var bytes = BFast.WriteBFastToBytes(One1GBBuffer); - TestBFastBytes(bytes); - var tmp = BFast.ReadBFast(bytes).ToArray(); - Assert.AreEqual(1, tmp.Length); - Assert.AreEqual(tmp.Select(x => x.Name).ToArray(), new[] { "0" }); - Assert.AreEqual(tmp.Select(x => x.NumBytes()).ToArray(), Enumerable.Repeat((long)Gb, 1).ToArray()); - } - } - - public static BFastBuilder BFastWithSubs(int numBuffers, int numLevels, Func numBytes) - => Enumerable.Range(0, numBuffers).Aggregate(new BFastBuilder(), - (bld, i) => bld.Add(i.ToString(), - numLevels > 0 - ? BFastWithSubs(numBuffers, numLevels - 1, numBytes) - : BFastRoot(numBuffers, numBytes)) - ); - - public static BFastBuilder BFastRoot(int numBuffers, Func numBytes) - => Enumerable.Range(0, numBuffers).Aggregate(new BFastBuilder(), (bld, i) => bld.Add(i.ToString(), ByteArray(numBytes()).ToBuffer())); - - public static void ValidateBFast(byte[] buffer, BFastBuilder srcBuilder) - { - var bfast = BFast.ReadBFast(buffer).ToArray(); - - var names = srcBuilder.BufferNames().ToArray(); - var sizes = srcBuilder.BufferSizes().ToArray(); - var numBuffers = names.Count(); - // We should have the same number of buffers - AssertEquals(bfast.Length, numBuffers); - for (var i = 0; i < numBuffers; i++) - { - // Of equal size - AssertEquals(bfast[i].Name, names[i]); - AssertEquals(bfast[i].Data.Length, sizes[i]); - // And they might be sub-buffers - if (srcBuilder.Children[i].Item2 is BFastBuilder childBuilder) - ValidateBFast(bfast[i].ToBytes(), childBuilder); - } - } - - [Test] - public static void TestNestedBFast() - { - var random = new Random(1234567); - // Create a nested BFast structure 3 layers deep with randomly-sized buffers between 1 & 256 bytes size - var builder = BFastWithSubs(3, 3, () => random.Next(1, 256)); - // Create a buffer to recieve this structure; - var buffer = new byte[builder.GetSize()]; - var stream = new MemoryStream(buffer, true); - builder.Write(stream); - - // Now, lets try and deserialize these buffers: - ValidateBFast(buffer, builder); - } - - public static void AssertEquals(T x, T y) - { - if (!x.Equals(y)) - throw new Exception($"Expected value {x} but instead got {y}"); - } - - /// - /// This test cannot be run from the test runner, because the App.Config option - /// has to be enabled from within the host program. - /// - public static void ReallyBigTest() - { - var xs = new Vector3[500 * 1000 * 1000]; - for (var i = 0; i < xs.Length; ++i) - xs[i] = new Vector3(i, i, i); - var filePath = Path.Combine(Path.GetTempPath(), "really_big_test.bfast"); - using (var stream = File.OpenWrite(filePath)) - stream.WriteBFast(new[] { ("buffer", xs) }); - - var name = ""; - Vector3[] ys; - using (var stream = File.OpenRead(filePath)) - { - var buffers = BFast.ReadBFast(stream).ToArray(); - if (buffers.Length != 1) - throw new Exception($"Expected exactly one buffer, not {buffers.Length}"); - (name, ys) = (buffers[0].Name, buffers[1].AsArray()); - } - if (name != "buffer") - throw new Exception($"Expected name of buffer to be buffer not {name}"); - AssertEquals(xs.Length, ys.Length); - AssertEquals(xs[0], ys[0]); - AssertEquals(xs[1], ys[1]); - AssertEquals(xs[xs.Length - 1], ys[ys.Length - 1]); - } - } -} diff --git a/src/cs/bfast/Vim.BFast.Tests/BFastTests.cs b/src/cs/bfast/Vim.BFast.Tests/BFastTests.cs new file mode 100644 index 00000000..7e4a9742 --- /dev/null +++ b/src/cs/bfast/Vim.BFast.Tests/BFastTests.cs @@ -0,0 +1,576 @@ +using NUnit.Framework; +using NUnit.Framework.Constraints; +using System.Data; +using Vim.BFastLib.Core; +using Vim.Util.Tests; + +namespace Vim.BFastLib.Tests +{ + public class BFastTests + { + public static string ResultPath = Path.Combine(VimFormatRepoPaths.OutDir, "input.bfast"); + public static string ResultPath2 = Path.Combine(VimFormatRepoPaths.OutDir, "input.bfast"); + public static string ResidencePath = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + + BFast bfast; + + [SetUp] + public void Setup() + { + bfast = new BFast(); + + if (!Directory.Exists(VimFormatRepoPaths.OutDir)) + { + Directory.CreateDirectory(VimFormatRepoPaths.OutDir); + } + if (File.Exists(ResultPath)) + { + File.Delete(ResultPath); + } + if (File.Exists(ResultPath2)) + { + File.Delete(ResultPath2); + } + } + + private void TestBeforeAfter(Action method) + { + method(bfast); + + // Test that it also works after write/read + var next = new BFast(bfast.ToMemoryStream()); + method(next); + } + + private void TestBeforeAfter(Func method, IResolveConstraint constraint) + { + Assert.That(method(bfast), constraint); + + // Test that it also works after write/read + var next = new BFast(bfast.ToMemoryStream()); + Assert.That(method(next), constraint); + } + + private void TestBeforeAfterFile(Func method, IResolveConstraint constraint) + { + Assert.That(method(bfast), constraint); + using (var file = File.Open(ResultPath, FileMode.CreateNew)) + { + bfast.Write(file); + file.Seek(0, SeekOrigin.Begin); + + // Test that it also works after write/read + var next = new BFast(file); + Assert.That(method(next), constraint); + } + } + + #region empty + + [Test] + public void EmptyBFast_Has_No_Entries() + { + var bfast = new BFast(); + Assert.That(bfast.Entries.Count(), Is.EqualTo(0)); + } + + [Test] + public void EmptyBFast_GetArray_Returns_Null() + { + var bfast = new BFast(); + Assert.IsNull(bfast.GetArray("missing")); + } + + [Test] + public void EmptyBFast_GetBfast_Returns_Null() + { + var bfast = new BFast(); + Assert.IsNull(bfast.GetBFast("missing")); + } + + [Test] + public void EmptyBFast_GetEnumerable_Returns_Null() + { + var bfast = new BFast(); + Assert.IsNull(bfast.GetEnumerable("missing")); + } + + [Test] + public void EmptyBFast_Remove_Does_Nothing() + { + var bfast = new BFast(); + bfast.Remove("missing"); + } + + + [Test] + public void EmptyBFast_Writes_Header() + { + var bfast = new BFast(); + var stream = new MemoryStream(); + bfast.Write(stream); + + stream.Seek(0, SeekOrigin.Begin); + var raw = BFastHeader.FromStream(stream); + + Assert.That(raw.Ranges.Count, Is.EqualTo(0)); + } + #endregion + + #region enumerable + + [Test] + public void SetEnumerable_Adds_Entry() + { + bfast.SetEnumerable("A", () => new int[3] { 0, 1, 2 }); + TestBeforeAfter(b => b.Entries.Count(), Is.EqualTo(1)); + } + + [Test] + public void SetEnumerable_Then_GetEnumerable() + { + var expected = new int[3] { 0, 1, 2 }; + bfast.SetEnumerable("A", () => expected); + TestBeforeAfter(b => b.GetEnumerable("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetEnumerable_Then_GetEnumerable_Bytes() + { + bfast.SetEnumerable("A", () => new int[3] { 0, 1, 2 }); + var expected = (new int[3] { 0, 1, 2 }).SelectMany(i => BitConverter.GetBytes(i)); + TestBeforeAfter(b => b.GetEnumerable("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetEnumerable_Then_GetEnumerable_Float() + { + bfast.SetEnumerable("A", () => new int[3] { 0, 1, 2 }); + var expected = (new int[3] { 0, 1, 2 }).Select(i => BitConverter.Int32BitsToSingle(i)); + TestBeforeAfter(b => b.GetEnumerable("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetEnumerable_Then_GetArray() + { + bfast.SetEnumerable("A", () => new int[3] { 0, 1, 2 }); + var expected = new int[3] { 0, 1, 2 }; + TestBeforeAfter(b => b.GetArray("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetEnumerable_Then_GetArray_Bytes() + { + bfast.SetEnumerable("A", () => new int[3] { 0, 1, 2 }); + var expected = (new int[3] { 0, 1, 2 }).SelectMany(i => BitConverter.GetBytes(i)); + TestBeforeAfter(b => b.GetArray("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetEnumerable_Then_GetArray_Float() + { + bfast.SetEnumerable("A", () => new int[3] { 0, 1, 2 }); + var expected = (new int[3] { 0, 1, 2 }).Select(i => BitConverter.Int32BitsToSingle(i)); + // MemoryStream can't handle such size. + TestBeforeAfter(b => b.GetArray("A"), Is.EqualTo(expected)); + + } + + [Test] + public void SetEnumerable_Then_GetBFast_Throws() + { + bfast.SetEnumerable("A", () => new int[3] { 0, 1, 2 }); + TestBeforeAfter(b => { + Assert.That(() => b.GetBFast("A"), Throws.Exception); + }); + } + + [Test] + public void SetEnumerable_Then_GetBFast_ValidBytes() + { + var sub = new BFast(); + bfast.SetBFast("A", sub); + var bytes = bfast.GetArray("A"); + bfast.SetEnumerable("A",() => bytes); + + TestBeforeAfter(b => b.GetBFast("A"), Is.EqualTo(sub)); + } + + [Test, Explicit] + public void SetEnumerable_Then_GetEnumerable_Lots() + { + IEnumerable GetLots() + { + return Enumerable.Range(0, int.MaxValue).Concat(Enumerable.Range(0, 10)); + } + bfast.SetEnumerable("A", GetLots); + + TestBeforeAfterFile(b => b.GetEnumerable("A"), Is.EqualTo(GetLots())); + } + + #endregion + + #region array + [Test] + public void SetArray_Adds_Entry() + { + bfast.SetArray("A", new int[3] { 0, 1, 2 }); + TestBeforeAfter(b => b.Entries.Count(), Is.EqualTo(1)); + } + + [Test] + public void SetArray_Then_GetArray() + { + var array = new int[3] { 0, 1, 2 }; + bfast.SetArray("A", array); + TestBeforeAfter(b => b.GetArray("A"), Is.EqualTo(array)); + } + + [Test] + public void SetArray_Then_GetArray_Bytes() + { + var array = new int[3] { 0, 1, 2 }; + var expected = array.SelectMany(i => BitConverter.GetBytes(i)); + + bfast.SetArray("A", array); + TestBeforeAfter(b => bfast.GetArray("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetArray_Then_GetArray_Float() + { + var array = new int[3] { 0, 1, 2 }; + var expected = array.Select(i => BitConverter.Int32BitsToSingle(i)); + + bfast.SetArray("A", array); + TestBeforeAfter(b => bfast.GetArray("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetArray_Then_GetEnumerable() + { + var array = new int[3] { 0, 1, 2 }; + bfast.SetArray("A", array); + TestBeforeAfter(b => b.GetEnumerable("A"), Is.EqualTo(array)); + } + + [Test] + public void SetArray_Then_GetEnumerable_Bytes() + { + var array = new int[3] { 0, 1, 2 }; + var expected = array.SelectMany(i => BitConverter.GetBytes(i)); + + bfast.SetArray("A", array); + TestBeforeAfter(b => b.GetEnumerable("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetArray_Then_GetEnumerable_Float() + { + var array = new int[3] { 0, 1, 2 }; + var expected = array.Select(i => BitConverter.Int32BitsToSingle(i)); + + bfast.SetArray("A", array); + var result = bfast.GetEnumerable("A"); + } + + [Test] + public void SetArray_Then_GetBFast_Throws() + { + bfast.SetArray("A", new int[3] { 0, 1, 2 }); + TestBeforeAfter(b => { + Assert.That(() => b.GetBFast("A"), Throws.Exception); + }); + } + + [Test] + public void SetArray_Then_SetArray_Replaces() + { + var ints = new int[3] { 0, 1, 2 }; + var floats = new float[3] { 0.1f, 0.2f, 0.3f }; + bfast.SetArray("A", ints); + bfast.SetArray("A", floats); + TestBeforeAfter(b => { + Assert.That(b.GetArray("A"), Is.EqualTo(floats)); + Assert.That(b.GetArray("A"), Is.Not.EqualTo(ints)); + }); + } + + [Test] + public void SetArray_Then_SetBFast_Replaces() + { + bfast.SetArray("A", new int[3] { 0, 1, 2 }); + bfast.SetBFast("A", new BFast()); + TestBeforeAfter(b => + { + // That's the bfast read as an ints. + Assert.That(b.GetArray("A").Length, Is.GreaterThan(3)); + Assert.That(b.GetBFast("A"), Is.EqualTo(new BFast())); + }); + } + #endregion + + [Test] + public void SetBFast_Adds_Entry() + { + bfast.SetBFast("A", new BFast()); + TestBeforeAfter(b => b.Entries.Count(), Is.EqualTo(1)); + } + + [Test] + public void SetBFast_Then_GetBFast_Returns_Same() + { + var expected = new BFast(); + bfast.SetBFast("A", expected); + TestBeforeAfter(b => b.GetBFast("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetBFast_Then_GetBFast_Nested() + { + using (var file = File.Open(ResidencePath, FileMode.Open)) + { + var (b1, b2) = (new BFast(), new BFast()); + b1.SetBFast("b2", b2); + bfast.SetBFast("b1", b1); + + var mem = bfast.ToMemoryStream(); + var r = new BFast(mem); + var r1 = r.GetBFast("b1"); + var r2 = r1.GetBFast("b2"); + + Assert.NotNull(r); + Assert.NotNull(r1); + Assert.NotNull(r2); + } + } + + #region compress + [Test] + public void Compression_Decompress_Uncompressed_Returns_Throws() + { + var expected = new BFast(); + bfast.SetBFast("A", expected); + TestBeforeAfter(b => + { + Assert.That(() => b.GetBFast("A", decompress: true), Throws.Exception); + }); + } + + [Test] + public void Compression_Get_Compressed_Returns_Null() + { + var expected = new BFast(); + bfast.SetBFast("A", expected, compress: true); + TestBeforeAfter(b => + { + Assert.That(() => b.GetBFast("A"), Throws.Exception); + }); + } + + [Test] + public void Compression_Get_Uncompressed_Works() + { + // This is tested by the bfast tests. + } + + [Test] + public void Compression_Decompress_Compressed_Works() + { + var ints = new int[3] { 0, 1, 2 }; + + var bfastA = new BFast(); + bfastA.SetArray("B", ints); + bfast.SetBFast("A", bfastA, compress: true); + + TestBeforeAfter((b) => + { + var result = b.GetBFast("A", decompress: true); + var b2 = result.GetArray("B"); + + Assert.That(result.Entries.Count(), Is.EqualTo(1)); + Assert.That(b2, Is.EqualTo(ints)); + }); + } + #endregion + + #region bfast + + [Test] + public void SetBFast_Then_SetBFast_Replaces() + { + var bfastA = new BFast(); + bfast.SetBFast("A", bfastA); + + var bfastB = new BFast(); + bfastB.SetArray("A", new int[] { 1, 2, 3 }); + bfast.SetBFast("A", bfastB); + + TestBeforeAfter((b) => + { + var result = b.GetBFast("A"); + Assert.That(bfastA, Is.Not.EqualTo(bfastB)); + Assert.That(result, Is.Not.EqualTo(bfastA)); + Assert.That(result, Is.EqualTo(bfastB)); + }); + } + + [Test] + public void SetBFast_Then_SetArray_Replaces() + { + var ints = new int[3] { 0, 1, 2 }; + bfast.SetBFast("A", new BFast()); + bfast.SetArray("A", ints); + TestBeforeAfter((b) => + { + Assert.That(() => b.GetBFast("A"), Throws.Exception); + Assert.That(b.GetArray("A"), Is.EqualTo(ints)); + }); + + } + #endregion + + [Test] + public void Remove_Missing_DoesNothing() + { + TestBeforeAfter((b) => + { + b.Remove("A"); + Assert.That(b.Entries.Count() == 0); + }); + } + + [Test] + public void Remove_Array() + { + bfast.SetArray("A", new int[3] { 0, 1, 2 }); + bfast.Remove("A"); + TestBeforeAfter((b) => + { + Assert.IsNull(b.GetArray("A")); + Assert.That(b.Entries.Count() == 0); + }); + } + + [Test] + public void Remove_BFast() + { + bfast.SetBFast("A", new BFast()); + bfast.Remove("A"); + + TestBeforeAfter((b) => + { + Assert.IsNull(bfast.GetBFast("A")); + Assert.That(bfast.Entries.Count() == 0); + }); + } + + [Test] + public void Removed_InChild_Not_Written() + { + using (var residence = File.OpenRead(ResidencePath)) + { + var input = new BFast(residence); + var geometry = input.GetBFast("geometry"); + geometry.Remove("g3d:vertex:position:0:float32:3"); + input.SetBFast("geometry", geometry); + input.Write(ResultPath); + } + + using (var stream = File.OpenRead(ResultPath)) + { + var bfast = new BFast(stream); + var geometry = bfast.GetBFast("geometry"); + Assert.That(bfast.Entries.Count() == 5); + Assert.That(geometry.Entries.Count() == 16); + Assert.IsNull(geometry.GetArray("g3d:vertex:position:0:float32:3")); + } + } + + [Test] + public void Write_Then_Read_NestedBFast() + { + var bfast = new BFast(); + var child = new BFast(); + var grandChild = new BFast(); + + bfast.SetBFast("child", child); + child.SetBFast("grandChild", grandChild); + bfast.Write(ResultPath); + + using (var stream = File.OpenRead(ResultPath)) + { + var other = new BFast(stream); + var child2 = other.GetBFast("child"); + var grandChild2 = child2.GetBFast("grandChild"); + + Assert.That(other.Entries.Count() == 1); + Assert.That(child2.Entries.Count() == 1); + Assert.That(grandChild2.Entries.Count() == 0); + } + } + + [Test] + public void Write_Then_Read_NestedBFast_WithArray() + { + var bfast = new BFast(); + var child = new BFast(); + var grandChild = new BFast(); + + bfast.SetBFast("child", child); + child.SetBFast("grandChild", grandChild); + grandChild.SetArray("A", new int[3] { 0, 1, 2 }); + + + bfast.Write(ResultPath); + using (var stream = File.OpenRead(ResultPath)) + { + var other = new BFast(stream); + var child2 = other.GetBFast("child"); + var grandChild2 = child2.GetBFast("grandChild"); + var result = grandChild2.GetArray("A"); + + Assert.That(other.Entries.Count() == 1); + Assert.That(child2.Entries.Count() == 1); + Assert.That(grandChild2.Entries.Count() == 1); + Assert.That(result, Is.EqualTo(new int[3] { 0, 1, 2 })); + } + } + + [Test] + public void Write_Then_Read_Mixed_Sources() + { + var basic = new BFast(); + var dummy = new MemoryStream(); + basic.SetArray("ints", new int[1] { 1 }); + basic.SetArray("floats", new float[1] { 2.0f }); + basic.Write(dummy); + + using (var residence = File.OpenRead(ResidencePath)) + { + dummy.Seek(0, SeekOrigin.Begin); + var input = new BFast(dummy); + + var inputResidence = new BFast(residence); + var output = new BFast(); + + output.SetBFast("input", input); + output.SetBFast("residence", inputResidence); + output.Write(ResultPath2); + } + + using (var stream = File.OpenRead(ResultPath2)) + { + var bfast = new BFast(stream); + var input = bfast.GetBFast("input"); + var residence = bfast.GetBFast("residence"); + var geometry = residence.GetBFast("geometry"); + + Assert.That(bfast.Entries.Count() == 2); + Assert.That(input.Entries.Count() == 2); + Assert.That(residence.Entries.Count() == 5); + Assert.That(geometry.Entries.Count() == 17); + } + } + } +} diff --git a/src/cs/bfast/Vim.BFast.Tests/Vim.BFast.Tests.csproj b/src/cs/bfast/Vim.BFast.Tests/Vim.BFast.Tests.csproj index 3ce7dc39..c7ddf233 100644 --- a/src/cs/bfast/Vim.BFast.Tests/Vim.BFast.Tests.csproj +++ b/src/cs/bfast/Vim.BFast.Tests/Vim.BFast.Tests.csproj @@ -1,18 +1,29 @@  - - net6.0 - false - + + net6.0 + enable + enable - - - - - + false + true + + + + True + + + - - - + + + + + + + + + + diff --git a/src/cs/bfast/Vim.BFast/BFast.cs b/src/cs/bfast/Vim.BFast/BFast.cs deleted file mode 100644 index 885fe582..00000000 --- a/src/cs/bfast/Vim.BFast/BFast.cs +++ /dev/null @@ -1,439 +0,0 @@ -/* - BFAST - Binary Format for Array Streaming and Transmission - Copyright 2019, VIMaec LLC - Copyright 2018, Ara 3D, Inc. - Usage licensed under terms of MIT License - https://github.com/vimaec/bfast - - The BFAST format is a simple, generic, and efficient representation of - buffers (arrays of binary data) with optional names. - - It can be used in place of a zip when compression is not required, or when a simple protocol - is required for transmitting data to/from disk, between processes, or over a network. -*/ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; - -namespace Vim.BFast -{ - /// - /// Callback function allows clients to control writing the data to the output stream - /// - public delegate long BFastWriterFn(Stream writingStream, int bufferIdx, string bufferName, long bytesToWrite); - - /// - /// Wraps an array of byte buffers encoding a BFast structure and provides validation and safe access to the memory. - /// The BFAST file/data format is structured as follows: - /// * File header - Fixed size file descriptor - /// * Ranges - An array of pairs of offsets that point to the begin and end of each data arrays - /// * Array data - All of the array data is contained in this section. - /// - public static class BFast - { - /// - /// Given a position in the stream, tells us where the the next aligned position will be, if it the current position is not aligned. - /// - public static long ComputeNextAlignment(long n) - => IsAligned(n) ? n : n + Constants.ALIGNMENT - (n % Constants.ALIGNMENT); - - /// - /// Given a position in the stream, computes how much padding is required to bring the value to an aligned point. - /// - public static long ComputePadding(long n) - => ComputeNextAlignment(n) - n; - - /// - /// Computes the padding requires after the array of BFastRanges are written out. - /// - /// - /// - public static long ComputePadding(BFastRange[] ranges) - => ComputePadding(BFastPreamble.Size + ranges.Length * BFastRange.Size); - - /// - /// Given a position in the stream, tells us whether the position is aligned. - /// - public static bool IsAligned(long n) - => n % Constants.ALIGNMENT == 0; - - /// - /// Writes n zero bytes. - /// - public static void WriteZeroBytes(this BinaryWriter bw, long n) - { - for (var i = 0L; i < n; ++i) - bw.Write((byte)0); - } - - /// - /// Checks that the stream (if seekable) is well aligned - /// - public static void CheckAlignment(Stream stream) - { - if (!stream.CanSeek) - return; - // TODO: Check with CD: Should we bail out here? This means that any - // alignment checks for a currently-writing stream are effectively ignored. - if (stream.Position == stream.Length) - return; - if (!IsAligned(stream.Position)) - throw new Exception($"Stream position {stream.Position} is not well aligned"); - } - - /// - /// Converts a collection of strings, into a null-separated byte[] array - /// - public static byte[] PackStrings(this IEnumerable strings) - { - var r = new List(); - foreach (var name in strings) - { - var bytes = Encoding.UTF8.GetBytes(name); - r.AddRange(bytes); - r.Add(0); - } - return r.ToArray(); - } - - /// - /// Converts a byte[] array encoding a collection of strings separate by NULL into an array of string - /// - public static string[] UnpackStrings(this byte[] bytes) - { - var r = new List(); - if (bytes.Length == 0) - return r.ToArray(); - var prev = 0; - for (var i = 0; i < bytes.Length; ++i) - { - if (bytes[i] == 0) - { - r.Add(Encoding.UTF8.GetString(bytes, prev, i - prev)); - prev = i + 1; - } - } - if (prev < bytes.Length) - r.Add(Encoding.UTF8.GetString(bytes, prev, bytes.Length - prev)); - return r.ToArray(); - } - - /// - /// Creates a BFAST structure, without any actual data buffers, from a list of sizes of buffers (not counting the name buffer). - /// Used as an intermediate step to create a BFAST. - /// - public static BFastHeader CreateBFastHeader(this long[] bufferSizes, string[] bufferNames) - { - if (bufferNames.Length != bufferSizes.Length) - throw new Exception($"The number of buffer sizes {bufferSizes.Length} is not equal to the number of buffer names {bufferNames.Length}"); - - var header = new BFastHeader - { - Names = bufferNames - }; - header.Preamble.Magic = Constants.Magic; - header.Preamble.NumArrays = bufferSizes.Length + 1; - - // Allocate the data for the ranges - header.Ranges = new BFastRange[header.Preamble.NumArrays]; - header.Preamble.DataStart = ComputeNextAlignment(header.Preamble.RangesEnd); - - var nameBufferLength = PackStrings(bufferNames).LongLength; - var sizes = (new[] { nameBufferLength }).Concat(bufferSizes).ToArray(); - - // Compute the offsets for the data buffers - var curIndex = header.Preamble.DataStart; - var i = 0; - foreach (var size in sizes) - { - curIndex = ComputeNextAlignment(curIndex); - Debug.Assert(IsAligned(curIndex)); - - header.Ranges[i].Begin = curIndex; - curIndex += size; - - header.Ranges[i].End = curIndex; - i++; - } - - // Finish with the header - // Each buffer we contain is padded to ensure the next one - // starts on alignment, so we pad our DataEnd to reflect this reality - header.Preamble.DataEnd = ComputeNextAlignment(curIndex); - - // Check that everything adds up - return header.Validate(); - } - - /// - /// Checks that the header values are sensible, and throws an exception otherwise. - /// - public static BFastPreamble Validate(this BFastPreamble preamble) - { - if (preamble.Magic != Constants.SameEndian && preamble.Magic != Constants.SwappedEndian) - throw new Exception($"Invalid magic number {preamble.Magic}"); - - if (preamble.DataStart < BFastPreamble.Size) - throw new Exception($"Data start {preamble.DataStart} cannot be before the file header size {BFastPreamble.Size}"); - - if (preamble.DataStart > preamble.DataEnd) - throw new Exception($"Data start {preamble.DataStart} cannot be after the data end {preamble.DataEnd}"); - - if (!IsAligned(preamble.DataEnd)) - throw new Exception($"Data end {preamble.DataEnd} should be aligned"); - - if (preamble.NumArrays < 0) - throw new Exception($"Number of arrays {preamble.NumArrays} is not a positive number"); - - if (preamble.NumArrays > preamble.DataEnd) - throw new Exception($"Number of arrays {preamble.NumArrays} can't be more than the total size"); - - if (preamble.RangesEnd > preamble.DataStart) - throw new Exception($"End of range {preamble.RangesEnd} can't be after data-start {preamble.DataStart}"); - - return preamble; - } - - /// - /// Checks that the header values are sensible, and throws an exception otherwise. - /// - public static BFastHeader Validate(this BFastHeader header) - { - var preamble = header.Preamble.Validate(); - var ranges = header.Ranges; - var names = header.Names; - - if (preamble.RangesEnd > preamble.DataStart) - throw new Exception($"Computed arrays ranges end must be less than the start of data {preamble.DataStart}"); - - if (ranges == null) - throw new Exception("Ranges must not be null"); - - var min = preamble.DataStart; - var max = preamble.DataEnd; - - for (var i = 0; i < ranges.Length; ++i) - { - var begin = ranges[i].Begin; - if (!IsAligned(begin)) - throw new Exception($"The beginning of the range is not well aligned {begin}"); - var end = ranges[i].End; - if (begin < min || begin > max) - throw new Exception($"Array offset begin {begin} is not in valid span of {min} to {max}"); - if (i > 0) - { - if (begin < ranges[i - 1].End) - throw new Exception($"Array offset begin {begin} is overlapping with previous array {ranges[i - 1].End}"); - } - - if (end < begin || end > max) - throw new Exception($"Array offset end {end} is not in valid span of {begin} to {max}"); - } - - if (names.Length < ranges.Length - 1) - throw new Exception($"Number of buffer names {names.Length} is not one less than the number of ranges {ranges.Length}"); - - return header; - } - - /// - /// Reads a BFAST from a file as a collection of named buffers. - /// - public static INamedBuffer[] Read(string filePath) - { - using (var stream = File.OpenRead(filePath)) - return Read(stream); - } - - /// - /// Reads a BFAST from a stream as a collection of named buffers. - /// - public static INamedBuffer[] Read(Stream stream) - => stream.ReadBFast().ToArray(); - - /// - /// Reads a BFAST buffer from a stream as a collection of named buffers. - /// This call limits the buffers to 2GB. - /// - public static IEnumerable ReadBFast(this Stream stream) - { - foreach (var br in stream.GetBFastBufferReaders()) - { - var s = br.Seek(); - yield return s.ReadArray((int)br.Size).ToNamedBuffer(br.Name); - } - } - - /// - /// Reads a BFAST from a stream as a collection of named buffers. - /// This call limits the buffers to 2GB. - /// - public static unsafe IEnumerable> ReadBFast(this Stream stream) where T : unmanaged - => stream.ReadBFast>((s, bufferName, bufferLength) - => s.ReadArray((int)(bufferLength / sizeof(T))).ToNamedBuffer(bufferName)) - .Select(item => item.Item2); - - /// - /// Reads a BFAST from a byte array as a collection of named buffers. - /// This call limits the buffers to 2GB. - /// - public static INamedBuffer[] ReadBFast(this byte[] bytes) - { - using (var stream = new MemoryStream(bytes)) - return ReadBFast(stream).ToArray(); - } - - /// - /// The total size required to put a BFAST in the header. - /// - public static long ComputeSize(long[] bufferSizes, string[] bufferNames) - => CreateBFastHeader(bufferSizes, bufferNames).Preamble.DataEnd; - - /// - /// Writes the BFAST header and name buffer to stream using the provided BinaryWriter. The BinaryWriter will be properly aligned by padding zeros - /// - public static BinaryWriter WriteBFastHeader(this Stream stream, BFastHeader header) - { - if (header.Ranges.Length != header.Names.Length + 1) - throw new Exception($"The number of ranges {header.Ranges.Length} must be equal to one more than the number of names {header.Names.Length}"); - var bw = new BinaryWriter(stream); - bw.Write(header.Preamble.Magic); - bw.Write(header.Preamble.DataStart); - bw.Write(header.Preamble.DataEnd); - bw.Write(header.Preamble.NumArrays); - foreach (var r in header.Ranges) - { - bw.Write(r.Begin); - bw.Write(r.End); - } - WriteZeroBytes(bw, ComputePadding(header.Ranges)); - - CheckAlignment(stream); - var nameBuffer = PackStrings(header.Names); - bw.Write(nameBuffer); - WriteZeroBytes(bw, ComputePadding(nameBuffer.LongLength)); - - CheckAlignment(stream); - return bw; - } - - /// - /// Enables a user to write a BFAST from an array of names, sizes, and a custom writing function. - /// The function will receive a BinaryWriter, the index of the buffer, and is expected to return the number of bytes written. - /// Simplifies the process of creating custom BinaryWriters, or writing extremely large arrays if necessary. - /// - public static void WriteBFast(this Stream stream, string[] bufferNames, long[] bufferSizes, BFastWriterFn onBuffer) - { - if (bufferSizes.Any(sz => sz < 0)) - throw new Exception("All buffer sizes must be zero or greater than zero"); - - if (bufferNames.Length != bufferSizes.Length) - throw new Exception($"The number of buffer names {bufferNames.Length} is not equal to the number of buffer sizes {bufferSizes}"); - - var header = CreateBFastHeader(bufferSizes, bufferNames); - stream.WriteBFast(header, bufferNames, bufferSizes, onBuffer); - } - - /// - /// Enables a user to write a BFAST from an array of names, sizes, and a custom writing function. - /// This is useful when the header is already computed. - /// - public static void WriteBFast(this Stream stream, BFastHeader header, string[] bufferNames, long[] bufferSizes, BFastWriterFn onBuffer) - { - stream.WriteBFastHeader(header); - CheckAlignment(stream); - stream.WriteBFastBody(header, bufferNames, bufferSizes, onBuffer); - } - - /// - /// Must be called after "WriteBFastHeader" - /// Enables a user to write the contents of a BFAST from an array of names, sizes, and a custom writing function. - /// The function will receive a BinaryWriter, the index of the buffer, and is expected to return the number of bytes written. - /// Simplifies the process of creating custom BinaryWriters, or writing extremely large arrays if necessary. - /// - public static void WriteBFastBody(this Stream stream, BFastHeader header, string[] bufferNames, long[] bufferSizes, BFastWriterFn onBuffer) - { - CheckAlignment(stream); - - if (bufferSizes.Any(sz => sz < 0)) - throw new Exception("All buffer sizes must be zero or greater than zero"); - - if (bufferNames.Length != bufferSizes.Length) - throw new Exception($"The number of buffer names {bufferNames.Length} is not equal to the number of buffer sizes {bufferSizes}"); - - // Then passes the binary writer for each buffer: checking that the correct amount of data was written. - for (var i = 0; i < bufferNames.Length; ++i) - { - CheckAlignment(stream); - var nBytes = bufferSizes[i]; - var pos = stream.CanSeek ? stream.Position : 0; - var nWrittenBytes = onBuffer(stream, i, bufferNames[i], nBytes); - if (stream.CanSeek) - { - if (stream.Position - pos != nWrittenBytes) - throw new NotImplementedException($"Buffer:{bufferNames[i]}. Stream movement {stream.Position - pos} does not reflect number of bytes claimed to be written {nWrittenBytes}"); - } - - if (nBytes != nWrittenBytes) - throw new Exception($"Number of bytes written {nWrittenBytes} not equal to expected bytes{nBytes}"); - var padding = ComputePadding(nBytes); - for (var j = 0; j < padding; ++j) - stream.WriteByte(0); - CheckAlignment(stream); - } - } - - public static unsafe long ByteSize(this T[] self) where T : unmanaged - => self.LongLength * sizeof(T); - - public static unsafe void WriteBFast(this Stream stream, IEnumerable<(string, T[])> buffers) where T : unmanaged - { - var xs = buffers.ToArray(); - BFastWriterFn writerFn = (writer, index, name, size) => - { - var initPosition = writer.Position; - writer.Write(xs[index].Item2); - return writer.Position - initPosition; - }; - - stream.WriteBFast( - xs.Select(b => b.Item1), - xs.Select(b => b.Item2.ByteSize()), - writerFn); - } - - public static void WriteBFast(this Stream stream, IEnumerable bufferNames, IEnumerable bufferSizes, BFastWriterFn onBuffer) - => WriteBFast(stream, bufferNames.ToArray(), bufferSizes.ToArray(), onBuffer); - - public static byte[] WriteBFastToBytes(IEnumerable bufferNames, IEnumerable bufferSizes, BFastWriterFn onBuffer) - { - // NOTE: we can't call "WriteBFast(Stream ...)" directly because it disposes the stream before we can convert it to an array - using (var stream = new MemoryStream()) - { - WriteBFast(stream, bufferNames.ToArray(), bufferSizes.ToArray(), onBuffer); - return stream.ToArray(); - } - } - - public static void WriteBFastToFile(string filePath, IEnumerable bufferNames, IEnumerable bufferSizes, BFastWriterFn onBuffer) - => File.OpenWrite(filePath).WriteBFast(bufferNames, bufferSizes, onBuffer); - - public static unsafe byte[] WriteBFastToBytes(this (string Name, T[] Data)[] buffers) where T : unmanaged - => WriteBFastToBytes( - buffers.Select(b => b.Name), - buffers.Select(b => b.Data.LongLength * sizeof(T)), - (writer, index, name, size) => - { - var initPosition = writer.Position; - writer.Write(buffers[index].Data); - return writer.Position - initPosition; - }); - - public static BFastBuilder ToBFastBuilder(this IEnumerable buffers) - => new BFastBuilder().Add(buffers); - } -} diff --git a/src/cs/bfast/Vim.BFast/BFast/BFast.cs b/src/cs/bfast/Vim.BFast/BFast/BFast.cs new file mode 100644 index 00000000..880251b3 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/BFast/BFast.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Vim.BFastLib.Core; + +namespace Vim.BFastLib +{ + /// + /// Main API to read and write bfast content. + /// + public class BFast : IBFastNode + { + private readonly Dictionary _children = new Dictionary(); + + /// + /// Returns all buffer names in this bfast. + /// + public IEnumerable Entries => _children.Keys; + private IEnumerable<(string name, IWritable buffer)> Writables => _children.Select(kvp => (kvp.Key, kvp.Value as IWritable)); + + public BFast() { } + public BFast(Stream stream) + { + var nodes = GetBFastNodes(stream); + _children = nodes.ToDictionary(c => c.name, c => new CompressibleNode(c.value)); + } + + /// + /// Sets or overrides a bfast value at given name. + /// + public void SetBFast(string name, BFast bfast, bool compress = false) + { + if (bfast == null) + { + _children.Remove(name); + return; + } + + _children[name] = new CompressibleNode(bfast, compress); + } + + /// + /// Sets or overrides an enumerable value at given name. + /// + public void SetEnumerable(string name, Func> enumerable) where T : unmanaged + { + if (enumerable == null) + { + _children.Remove(name); + return; + } + _children[name] = new CompressibleNode(new BFastEnumerableNode(enumerable)); + } + + /// + /// Sets or overrides an array value at given name. + /// + public void SetArray(string name, T[] array) where T : unmanaged + { + if (array == null) + { + _children.Remove(name); + return; + } + _children[name] = new CompressibleNode(new BFastArrayNode(array)); + } + + /// + /// Tries to interpret the data at given name as a BFast and returns it. + /// Will throw if the data is not a bfast or if decompress doesnt match compression. + /// + public BFast GetBFast(string name, bool decompress = false) + { + var node = GetNode(name); + if (node == null) return null; + var n = node.GetNode(decompress); + return n.AsBFast(); + } + + /// + /// Tries to cast the data at given name as an enumerable of type T. + /// Will throw if the data cannot be cast. + /// + public IEnumerable GetEnumerable(string name) where T : unmanaged + { + if (!_children.ContainsKey(name)) return null; + return _children[name].GetNode().AsEnumerable(); + } + + /// + /// Tries to cast the data at given name as an array of type T. + /// Will throw if the data cannot be cast. + /// + public T[] GetArray(string name) where T : unmanaged + { + if (!_children.ContainsKey(name)) return null; + return _children[name].GetNode().AsArray(); + } + + private CompressibleNode GetNode(string name) + => _children.TryGetValue(name, out var value) ? value : null; + + /// + /// Remove the value at name so it won't be written. + /// + public void Remove(string name) + => _children.Remove(name); + + /// + /// Writes the current state to a stream using bfast format. + /// + public void Write(Stream stream) + { + var list = Writables.OrderBy(kvp => kvp.name).ToList(); + var strings = list.Select(n => n.name).ToArray(); + var buffers = list.Select(n => n.buffer).ToArray(); + var writer = new BFastWriter(strings, buffers); + writer.Write(stream); + } + + /// + /// Writes the current state to a new file using bfast format. + /// + public void Write(string path) + { + using (var file = new FileStream(path, FileMode.Create)) + { + Write(file); + } + } + + BFast IBFastNode.AsBFast() + { + return this; + } + + T[] IBFastNode.AsArray() + { + using (var mem = ToMemoryStream()) + { + return mem.ReadArray(); + } + } + + IEnumerable IBFastNode.AsEnumerable() + { + return (this as IBFastNode).AsArray(); + } + + private static IEnumerable<(string name, BFastStreamNode value)> GetBFastNodes(Stream stream) + { + var offset = stream.Position; + var raw = BFastHeader.FromStream(stream); + foreach (var kvp in raw.Ranges) + { + var node = new BFastStreamNode( + stream, + kvp.Value.OffsetBy(offset) + ); + + yield return (kvp.Key, node); + } + } + + /// + /// Writes the current bfast to a new memory streams + /// The stream is returned at position 0. + /// + public MemoryStream ToMemoryStream() + { + var stream = new MemoryStream(); + Write(stream); + stream.Seek(0, SeekOrigin.Begin); + return stream; + } + + public override bool Equals(object obj) + { + if (obj is BFast) + { + return Equals((BFast)obj); + } + return false; + } + + public bool Equals(BFast other) + { + var a = (this as IBFastNode).AsEnumerable(); + var b = (other as IBFastNode).AsEnumerable(); + return a.SequenceEqual(b); + } + + public override int GetHashCode() => (this as IBFastNode).AsEnumerable().GetHashCode(); + } +} + diff --git a/src/cs/bfast/Vim.BFast/BFast/BFastArrayNode.cs b/src/cs/bfast/Vim.BFast/BFast/BFastArrayNode.cs new file mode 100644 index 00000000..cc6a06da --- /dev/null +++ b/src/cs/bfast/Vim.BFast/BFast/BFastArrayNode.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Vim.BFastLib.Core; + +namespace Vim.BFastLib +{ + public class BFastArrayNode : IBFastNode where TData : unmanaged + { + private readonly TData[] _array; + + public BFastArrayNode(TData[] array) + { + _array = array; + } + + public T[] AsArray() where T : unmanaged + { + if (typeof(T) == typeof(TData)) + { + return _array as T[]; + } + return _array.Cast(); + } + + public BFast AsBFast() + { + try + { + return new BFast(_array.ToMemoryStream()); + } + catch (Exception e) + { + throw new Exception("Array data is not a valid BFast.", e); + } + } + + public IEnumerable AsEnumerable() where T: unmanaged { + return (_array as IEnumerable).Cast(); + } + + public void Write(Stream stream) + { + stream.Write(_array); + } + } +} diff --git a/src/cs/bfast/Vim.BFast/BFast/BFastEnumerableNode.cs b/src/cs/bfast/Vim.BFast/BFast/BFastEnumerableNode.cs new file mode 100644 index 00000000..aa6f4eb7 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/BFast/BFastEnumerableNode.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Vim.BFastLib.Core; + +namespace Vim.BFastLib +{ + public class BFastEnumerableNode : IBFastNode where TNode : unmanaged + { + // We use a Func to prevent cases where the given IEnumerable can't iterated twice. + private readonly Func> _source; + + public BFastEnumerableNode(Func> source) + { + _source = source; + } + + public T[] AsArray() where T : unmanaged + { + if (typeof(T) == typeof(TNode)) + { + return _source().Cast().ToArray(); + } + else + { + return _source().Cast().ToArray(); + } + } + + public BFast AsBFast() + { + try + { + return new BFast(_source().ToMemoryStream()); + } + catch (Exception e) + { + throw new Exception("Enumerable data is not a valid BFast", e); + } + } + public IEnumerable AsEnumerable() where T : unmanaged + { + if (typeof(T) == typeof(TNode)) + { + return _source().Cast(); + } + else + { + return _source().Cast(); + } + } + + public void Write(Stream stream) + { + stream.Write(_source()); + } + } +} diff --git a/src/cs/bfast/Vim.BFast/BFast/BFastHelpers.cs b/src/cs/bfast/Vim.BFast/BFast/BFastHelpers.cs new file mode 100644 index 00000000..0aa910a0 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/BFast/BFastHelpers.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Vim.BFastLib +{ + public static class BFastHelpers + { + /// + /// Opens a file as a BFast, applies func to it and closes the file. + /// + public static T Read(string path, Func func) + { + using (var file = new FileStream(path, FileMode.Open)) + { + var bfast = new BFast(file); + return func(bfast); + } + } + } + + public static class BFastExtensions + { + /// + /// Returns an enumerable of all nodes of the BFast as NamedBuffers. + /// + public static IEnumerable ToNamedBuffers(this BFast bfast) + { + return bfast.Entries.Select(name => bfast.GetArray(name).ToNamedBuffer(name)); + } + + /// + /// Writes the current bfast to a new memory streams + /// The stream is returned at position 0. + /// + public static MemoryStream ToMemoryStream(this IBFastNode bfast) + { + var stream = new MemoryStream(); + bfast.Write(stream); + stream.Seek(0, SeekOrigin.Begin); + return stream; + } + } +} + diff --git a/src/cs/bfast/Vim.BFast/BFast/BFastStreamNode.cs b/src/cs/bfast/Vim.BFast/BFast/BFastStreamNode.cs new file mode 100644 index 00000000..457b8470 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/BFast/BFastStreamNode.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Vim.BFastLib.Core; + +namespace Vim.BFastLib +{ + public class BFastStreamNode : IBFastNode + { + private readonly Stream _stream; + private readonly BFastRange _range; + + public BFastStreamNode(Stream stream, BFastRange range) + { + _stream = stream; + _range = range; + } + + public BFast AsBFast() + { + _stream.Seek(_range.Begin, SeekOrigin.Begin); + try + { + return new BFast(_stream); + } + catch (Exception e) + { + throw new Exception("Requested data is not a valid BFast or is compressed and needs decompression.", e); + } + } + + public T[] AsArray() where T : unmanaged + { + _stream.Seek(_range.Begin, SeekOrigin.Begin); + return _stream.ReadArrayBytes(_range.Count); + } + + public IEnumerable AsEnumerable() where T : unmanaged + { + _stream.Seek(_range.Begin, SeekOrigin.Begin); + return _stream.ReadEnumerableByte(_range.Count); + } + + public void Write(Stream stream) + { + _stream.Seek(_range.Begin, SeekOrigin.Begin); + _stream.CopySome(stream, (int)_range.Count); + } + } +} diff --git a/src/cs/bfast/Vim.BFast/BFast/CompressibleNode.cs b/src/cs/bfast/Vim.BFast/BFast/CompressibleNode.cs new file mode 100644 index 00000000..7a85eeba --- /dev/null +++ b/src/cs/bfast/Vim.BFast/BFast/CompressibleNode.cs @@ -0,0 +1,83 @@ +using System.IO; +using System.IO.Compression; +using Vim.BFastLib.Core; + +namespace Vim.BFastLib +{ + /// + /// A wrapper around a IBFastNode that manages writing and reading using compression. + /// + public class CompressibleNode : IWritable + { + private readonly IBFastNode _node; + private readonly bool _compress; + + public CompressibleNode(IBFastNode node, bool compress = false) + { + _node = node; + _compress = compress; + } + + public void Write(Stream stream) + { + if (_compress) + { + WriteCompress(stream); + } + else + { + _node.Write(stream); + } + } + + /// + /// Returns the node after it is decompressed if needed. + /// Will throw if decompress argument doesnt match compression state. + /// + public IBFastNode GetNode(bool decompress = false) + { + if (decompress) + { + if (_node is BFastStreamNode) + { + return Decompress(); + } + if (!_compress) + { + throw new System.Exception("Cannot uncompress non-compressed data."); + } + return _node; + } + if(_compress) + { + throw new System.Exception("Compressed data needs to be decompressed."); + } + return _node; + } + + private IBFastNode Decompress() + { + // This memory stream is not disposed. But it's ok. + // It really is just an array under the hood. + // https://stackoverflow.com/questions/4274590/memorystream-close-or-memorystream-dispose + var output = new MemoryStream(); + + using (var input = _node.ToMemoryStream()) + using (var compress = new DeflateStream(input, CompressionMode.Decompress, true)) + { + compress.CopyTo(output); + output.Seek(0, SeekOrigin.Begin); + return new BFastStreamNode(output, output.FullRange()); + } + } + + private void WriteCompress(Stream stream) + { + using (var input = _node.ToMemoryStream()) + using (var compress = new DeflateStream(stream, CompressionMode.Compress, true)) + { + input.CopyTo(compress); + } + } + } +} diff --git a/src/cs/bfast/Vim.BFast/BFast/IBFastNode.cs b/src/cs/bfast/Vim.BFast/BFast/IBFastNode.cs new file mode 100644 index 00000000..5d27c59e --- /dev/null +++ b/src/cs/bfast/Vim.BFast/BFast/IBFastNode.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.IO; + +namespace Vim.BFastLib +{ + public interface IWritable + { + /// + /// Writes the current data to the given stream. + /// + void Write(Stream stream); + } + + public interface IBFastNode : IWritable + { + /// + /// Tries to cast node data as an array of T. + /// + T[] AsArray() where T : unmanaged; + + /// + /// Tries to cast node data as an enumerable of T. + /// AsEnumerable() where T : unmanaged; + + /// + /// Tries to interpret node data as a BFast. + /// - /// Represents a BFAST buffer whose stream can be read after calling Seek(). - /// - public class BFastBufferReader - { - /// - /// The seekable stream from which the buffer can be read. - /// - private readonly Stream _stream; - - /// - /// The start position of the buffer in the stream. - /// - private readonly long _startPosition; - - /// - /// The size in bytes of the buffer. - /// - public readonly long Size; - - /// - /// The buffer name. - /// - public readonly string Name; - - /// - /// Deconstruct operator - /// - public void Deconstruct(out string name, out long size) - => (name, size) = (Name, Size); - - /// - /// Constructor. - /// - public BFastBufferReader(Stream stream, string name, long startPosition, long size) - { - _stream = stream; - _startPosition = startPosition; - Size = size; - Name = name; - } - - /// - /// Seeks to the start of the BFAST buffer and returns the stream. - /// - public Stream Seek() - { - _stream.Seek(_startPosition, SeekOrigin.Begin); - BFast.CheckAlignment(_stream); - return _stream; - } - } - - public static class BFastBufferReaderExtensions - { - /// - /// Reads the preamble, the ranges, and the names of the rest of the buffers. - /// - public static BFastHeader ReadBFastHeader(this Stream stream) - { - var r = new BFastHeader(); - var br = new BinaryReader(stream); - - if (stream.Length - stream.Position < sizeof(long) * 4) - throw new Exception("Stream too short"); - - r.Preamble = new BFastPreamble - { - Magic = br.ReadInt64(), - DataStart = br.ReadInt64(), - DataEnd = br.ReadInt64(), - NumArrays = br.ReadInt64(), - }.Validate(); - - r.Ranges = stream.ReadArray((int)r.Preamble.NumArrays); - - var padding = BFast.ComputePadding(r.Ranges); - br.ReadBytes((int)padding); - BFast.CheckAlignment(br.BaseStream); - - var nameBytes = br.ReadBytes((int)r.Ranges[0].Count); - r.Names = nameBytes.UnpackStrings(); - - padding = BFast.ComputePadding(r.Ranges[0].End); - br.ReadBytes((int)padding); - BFast.CheckAlignment(br.BaseStream); - - return r.Validate(); - } - - /// - /// Returns a list of BFAST buffer readers in the stream. - /// Assumes the stream's current position designates a BFAST header. - /// - public static IReadOnlyList GetBFastBufferReaders( - this Stream stream, - Func filterFn = null) - { - var result = new List(); - - using (var seekContext = new SeekContext(stream)) - { - // Read the header - var header = stream.ReadBFastHeader(); - BFast.CheckAlignment(stream); - - // Create a BFastBufferReader for each range. - for (var i = 1; i < header.Ranges.Length; ++i) - { - var range = header.Ranges[i]; - var name = header.Names[i - 1]; - - var startSeekPosition = seekContext.OriginalSeekPosition + range.Begin; - var size = range.End - range.Begin; - - var bfastBufferReader = new BFastBufferReader(seekContext.Stream, name, startSeekPosition, size); - - if (filterFn?.Invoke(bfastBufferReader) ?? true) - { - result.Add(bfastBufferReader); - } - } - } - - return result; - } - - /// - /// Returns a BFAST buffer reader corresponding to the given buffer name. - /// Returns null if the given buffer name was not found or if the buffer name is null or empty. - /// - public static BFastBufferReader GetBFastBufferReader(this Stream stream, string bufferName) - => string.IsNullOrEmpty(bufferName) - ? null - : stream.GetBFastBufferReaders(br => br.Name == bufferName).FirstOrDefault(); - - /// - /// Reads a BFAST stream and returns a list of labeled results. - /// - public static List<(string Label, T Result)> ReadBFast( - this Stream stream, - Func onBuffer) - { - var result = new List<(string, T)>(); - - foreach (var br in stream.GetBFastBufferReaders()) - { - var name = br.Name; - var s = br.Seek(); - result.Add((name, onBuffer(s, name, br.Size))); - } - - return result; - } - - /// - /// Returns a named buffer corresponding to the given bufferName. Returns null if no buffer name is found. - /// This call limits the buffers to 2GB. - /// - public static NamedBuffer ReadBFastBuffer(this Stream stream, string bufferName) where T : unmanaged - { - var br = stream.GetBFastBufferReader(bufferName); - if (br == null) - return null; - - var s = br.Seek(); - return s.ReadArray((int)br.Size).ToNamedBuffer(br.Name); - } - } -} diff --git a/src/cs/bfast/Vim.BFast/BFastBuilder.cs b/src/cs/bfast/Vim.BFast/BFastBuilder.cs deleted file mode 100644 index 7c39221b..00000000 --- a/src/cs/bfast/Vim.BFast/BFastBuilder.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; - -namespace Vim.BFast -{ - /// - /// Anything that can be added to a BFAST must have a size and write to a stream. - /// - public interface IBFastComponent - { - long GetSize(); - void Write(Stream stream); - } - - /// - /// A wrapper around a buffer so that it can be used as a BFAST component - /// - public class BufferAsBFastComponent : IBFastComponent - { - public BufferAsBFastComponent(IBuffer buffer) - => Buffer = buffer; - public IBuffer Buffer { get; } - public void Write(Stream stream) => stream.Write(Buffer); - public long GetSize() => Buffer.NumBytes(); - } - - /// - /// Used to build BFASTs incrementally that contain named buffers and/or other BFASTs. - /// - public class BFastBuilder : IBFastComponent - { - public BFastHeader Header { get; private set; } - public long GetSize() => GetOrComputeHeader().Preamble.DataEnd; - - public List<(string, IBFastComponent)> Children { get; } = new List<(string, IBFastComponent)>(); - - public void Write(Stream stream) - => stream.WriteBFast(GetOrComputeHeader(), - BufferNames().ToArray(), - BufferSizes().ToArray(), - OnBuffer); - - public void Write(string filePath) - { - using (var stream = File.OpenWrite(filePath)) - Write(stream); - } - - public long OnBuffer(Stream stream, int index, string name, long size) - { - var (bufferName, x) = Children[index]; - Debug.Assert(name == bufferName); - Debug.Assert(size != GetSize()); - Debug.Assert(size == x.GetSize()); - x.Write(stream); - return size; - } - - public BFastHeader GetOrComputeHeader() - => Header ?? (Header = BFast.CreateBFastHeader( - BufferSizes().ToArray(), BufferNames().ToArray())); - - private BFastBuilder _add(string name, IBFastComponent component) - { - Header = null; - Children.Add((name, component)); - return this; - } - - public BFastBuilder Add(string name, IBFastComponent component) - => _add(name, component); - - public BFastBuilder Add(string name, IBuffer buffer) - => _add(name, new BufferAsBFastComponent(buffer)); - - public BFastBuilder Add(INamedBuffer buffer) - => Add(buffer.Name, buffer); - - public BFastBuilder Add(IEnumerable buffers) - => buffers.Aggregate(this, (x, y) => x.Add(y)); - - public BFastBuilder Add(string name, IEnumerable buffers) - => Add(name, new BFastBuilder().Add(buffers)); - - public IEnumerable BufferNames() - => Children.Select(x => x.Item1); - - public IEnumerable BufferSizes() - => Children.Select(x => x.Item2.GetSize()); - } -} diff --git a/src/cs/bfast/Vim.BFast/BFastStructs.cs b/src/cs/bfast/Vim.BFast/BFastStructs.cs deleted file mode 100644 index 46cb0dde..00000000 --- a/src/cs/bfast/Vim.BFast/BFastStructs.cs +++ /dev/null @@ -1,113 +0,0 @@ -/* - BFAST - Binary Format for Array Streaming and Transmission - Copyright 2019, VIMaec LLC - Copyright 2018, Ara 3D, Inc. - Usage licensed under terms of MIT License - https://github.com/vimaec/bfast - - The BFAST format is a simple, generic, and efficient representation of - buffers (arrays of binary data) with optional names. - - It can be used in place of a zip when compression is not required, or when a simple protocol - is required for transmitting data to/from disk, between processes, or over a network. -*/ - -using System.Linq; -using System.Runtime.InteropServices; - -namespace Vim.BFast -{ - /// - /// This contains the BFAST data loaded or written from disk. - /// - public class BFastHeader - { - public BFastPreamble Preamble = new BFastPreamble(); - public BFastRange[] Ranges; - public string[] Names; - - public override bool Equals(object o) - => o is BFastHeader other && Equals(other); - - public bool Equals(BFastHeader other) - => Preamble.Equals(other.Preamble) && - Ranges.Length == other.Ranges.Length && - Ranges.Zip(other.Ranges, (x, y) => x.Equals(y)).All(x => x) && - Names.Zip(other.Names, (x, y) => x.Equals(y)).All(x => x); - } - - /// - /// Constants. - /// - public static class Constants - { - public const long Magic = 0xBFA5; - - // https://en.wikipedia.org/wiki/Endianness - public const long SameEndian = Magic; - public const long SwappedEndian = 0xA5BFL << 48; - - /// - /// Data arrays are aligned to 64 bytes, so that they can be cast directly to AVX-512 registers. - /// This is useful for efficiently working with floating point data. - /// - public const long ALIGNMENT = 64; - } - - /// - /// This tells us where a particular array begins and ends in relation to the beginning of a file. - /// * Begin must be less than or equal to End. - /// * Begin must be greater than or equal to DataStart - /// * End must be less than or equal to DataEnd - /// - [StructLayout(LayoutKind.Explicit, Pack = 8, Size = 16)] - public struct BFastRange - { - [FieldOffset(0)] public long Begin; - [FieldOffset(8)] public long End; - - public long Count => End - Begin; - public static long Size = 16; - - public override bool Equals(object x) - => x is BFastRange other && Equals(other); - - public bool Equals(BFastRange other) - => Begin == other.Begin && End == other.End; - } - - /// - /// The header contains a magic number, the begin and end indices of data, and the number of arrays. - /// - [StructLayout(LayoutKind.Explicit, Pack = 8, Size = 32)] - public struct BFastPreamble - { - [FieldOffset(0)] public long Magic; // Either Constants.SameEndian or Constants.SwappedEndian depending on endianess of writer compared to reader. - [FieldOffset(8)] public long DataStart; // <= file size and >= ArrayRangesEnd and >= FileHeader.ByteCount - [FieldOffset(16)] public long DataEnd; // >= DataStart and <= file size - [FieldOffset(24)] public long NumArrays; // number of arrays - - /// - /// This is where the array ranges are finished. - /// Must be less than or equal to DataStart. - /// Must be greater than or equal to FileHeader.ByteCount - /// - public long RangesEnd => Size + NumArrays * 16; - - /// - /// The size of the FileHeader structure - /// - public static long Size = 32; - - /// - /// Returns true if the producer of the BFast file has the same endianness as the current library - /// - public bool SameEndian => Magic == Constants.SameEndian; - - public override bool Equals(object x) - => x is BFastPreamble other && Equals(other); - - public bool Equals(BFastPreamble other) - => Magic == other.Magic && DataStart == other.DataStart && DataEnd == other.DataEnd && NumArrays == other.NumArrays; - }; -} diff --git a/src/cs/bfast/Vim.BFast/BufferExtensions.cs b/src/cs/bfast/Vim.BFast/Buffers/BufferExtensions.cs similarity index 89% rename from src/cs/bfast/Vim.BFast/BufferExtensions.cs rename to src/cs/bfast/Vim.BFast/Buffers/BufferExtensions.cs index d07b8eb5..91fd57c4 100644 --- a/src/cs/bfast/Vim.BFast/BufferExtensions.cs +++ b/src/cs/bfast/Vim.BFast/Buffers/BufferExtensions.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Vim.BFastLib.Core; -namespace Vim.BFast +namespace Vim.BFastLib { /// /// Helper functions for working with buffers @@ -75,7 +76,7 @@ public static long NumBytes(this IBuffer buffer) => (long)buffer.NumElements() * buffer.ElementSize; public static Buffer ReadBufferFromNumberOfBytes(this Stream stream, long numBytes) where T : unmanaged - => stream.ReadArrayFromNumberOfBytes(numBytes).ToBuffer(); + => stream.ReadArrayBytes(numBytes).ToBuffer(); public static Buffer ReadBuffer(this Stream stream, int numElements) where T : unmanaged => stream.ReadArray(numElements).ToBuffer(); @@ -85,5 +86,15 @@ public static Buffer ReadBuffer(this Stream stream, int numBytes) public static void Write(this Stream stream, IBuffer buffer) => buffer.Write(stream); + + public static NamedBuffer Fill(this NamedBuffer buffer, T value) where T : unmanaged + { + var array = new T[buffer.Data.Length]; + for (var i = 0; i < array.Length; i++) + { + array[i] = value; + } + return array.ToNamedBuffer(buffer.Name); + } } } diff --git a/src/cs/bfast/Vim.BFast/Buffers.cs b/src/cs/bfast/Vim.BFast/Buffers/Buffers.cs similarity index 97% rename from src/cs/bfast/Vim.BFast/Buffers.cs rename to src/cs/bfast/Vim.BFast/Buffers/Buffers.cs index 8039f273..9f4205d1 100644 --- a/src/cs/bfast/Vim.BFast/Buffers.cs +++ b/src/cs/bfast/Vim.BFast/Buffers/Buffers.cs @@ -1,7 +1,8 @@ using System; using System.IO; +using Vim.BFastLib.Core; -namespace Vim.BFast +namespace Vim.BFastLib { /// /// Provides an interface to an object that manages a potentially large array of elements all of the same unmanaged type. diff --git a/src/cs/bfast/Vim.BFast/Core/BFastConstants.cs b/src/cs/bfast/Vim.BFast/Core/BFastConstants.cs new file mode 100644 index 00000000..c6934caf --- /dev/null +++ b/src/cs/bfast/Vim.BFast/Core/BFastConstants.cs @@ -0,0 +1,15 @@ +namespace Vim.BFastLib.Core +{ + /// + /// Constants. + /// + public static class BFastConstants + { + public const long Magic = 0xBFA5; + + // https://en.wikipedia.org/wiki/Endianness + public const long SameEndian = Magic; + public const long SwappedEndian = 0xA5BFL << 48; + + } +} diff --git a/src/cs/bfast/Vim.BFast/Core/BFastHeader.cs b/src/cs/bfast/Vim.BFast/Core/BFastHeader.cs new file mode 100644 index 00000000..bc0e62c4 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/Core/BFastHeader.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; + +namespace Vim.BFastLib.Core +{ + public class BFastHeader + { + public readonly BFastPreamble Preamble; + public IReadOnlyDictionary Ranges => _ranges; + private readonly Dictionary _ranges; + + public BFastHeader(BFastPreamble preamble, Dictionary ranges) + { + Preamble = preamble; + _ranges = ranges; + } + + /// + /// Reads the preamble, the ranges, and the names of the rest of the buffers. + /// + public static BFastHeader FromStream(Stream stream) + { + if (stream.Length - stream.Position < sizeof(long) * 4) + throw new Exception("Stream too short"); + + var offset = stream.Position; + + var preamble = stream.ReadValue(); + var ranges = stream.ReadArray((int)preamble.NumArrays); + + // In a lot of existing vim there is padding before the first buffer. + stream.Seek(offset + ranges[0].Begin, SeekOrigin.Begin); + var nameBytes = stream.ReadArray((int)ranges[0].Count); + var names = BFastStrings.Unpack(nameBytes); + + // Some old vim have duplicated buffers + // It is wrong but such is life. + MakeNamesUnique(names); + + var map = names + .Zip(ranges.Skip(1), (n, r) => (n, r)) + .ToDictionary(p => p.n, p => p.r); + + return new BFastHeader(preamble, map).Validate(); + } + + private static void MakeNamesUnique(string[] names) + { + var nameSet = new Dictionary(); + for (var i = 0; i < names.Length; i++) + { + if (nameSet.ContainsKey(names[i])) + { + var count = nameSet[names[i]]; + names[i] = names[i] + "_" + count; + Debug.WriteLine($"Duplicated Name {names[i]} in BFAST. Making name unique. This can result in unexpected behaviour."); + } + if (!nameSet.ContainsKey(names[i])) + { + nameSet.Add(names[i], i); + } + } + } + + public BFastHeader Validate() + { + Preamble.Validate(); + foreach (var range in _ranges.Values) + { + if (range.Begin < Preamble.DataStart) + { + throw new Exception("range.Begin must be larger than Data Start"); + } + if (range.End > Preamble.DataEnd) + { + throw new Exception("range.End must be smaller than Data End"); + } + } + return this; + } + } +} diff --git a/src/cs/bfast/Vim.BFast/Core/BFastPreamble.cs b/src/cs/bfast/Vim.BFast/Core/BFastPreamble.cs new file mode 100644 index 00000000..9aac30f8 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/Core/BFastPreamble.cs @@ -0,0 +1,68 @@ +using System; +using System.Runtime.InteropServices; + +namespace Vim.BFastLib.Core +{ + /// + /// The header contains a magic number, the begin and end indices of data, and the number of arrays. + /// + [StructLayout(LayoutKind.Explicit, Pack = 8, Size = 32)] + public struct BFastPreamble + { + [FieldOffset(0)] public long Magic; // Either Constants.SameEndian or Constants.SwappedEndian depending on endianess of writer compared to reader. + [FieldOffset(8)] public long DataStart; // <= file size and >= ArrayRangesEnd and >= FileHeader.ByteCount + [FieldOffset(16)] public long DataEnd; // >= DataStart and <= file size + [FieldOffset(24)] public long NumArrays; // number of arrays + + /// + /// The size of the FileHeader structure + /// + public static long Size = 32; + + /// + /// Returns true if the producer of the BFast file has the same endianness as the current library + /// + public bool SameEndian => Magic == BFastConstants.SameEndian; + + public override bool Equals(object x) + => x is BFastPreamble other && Equals(other); + + public bool Equals(BFastPreamble other) + => Magic == other.Magic && DataStart == other.DataStart && DataEnd == other.DataEnd && NumArrays == other.NumArrays; + + + /// + /// Checks that the header values are sensible, and throws an exception otherwise. + /// + public BFastPreamble Validate() + { + if (Magic != BFastConstants.SameEndian && Magic != BFastConstants.SwappedEndian) + throw new Exception($"Invalid magic number {Magic}"); + + if (DataStart < BFastPreamble.Size) + throw new Exception($"Data start {DataStart} cannot be before the file header size {BFastPreamble.Size}"); + + if (DataStart > DataEnd) + throw new Exception($"Data start {DataStart} cannot be after the data end {DataEnd}"); + + if (NumArrays < 0) + throw new Exception($"Number of arrays {NumArrays} should be at least one"); + + if (NumArrays > DataEnd) + throw new Exception($"Number of arrays {NumArrays} can't be more than the total size"); + + return this; + } + + public override int GetHashCode() + { + var hashCode = 275654494; + hashCode = hashCode * -1521134295 + Magic.GetHashCode(); + hashCode = hashCode * -1521134295 + DataStart.GetHashCode(); + hashCode = hashCode * -1521134295 + DataEnd.GetHashCode(); + hashCode = hashCode * -1521134295 + NumArrays.GetHashCode(); + hashCode = hashCode * -1521134295 + SameEndian.GetHashCode(); + return hashCode; + } + } +} diff --git a/src/cs/bfast/Vim.BFast/Core/BFastRange.cs b/src/cs/bfast/Vim.BFast/Core/BFastRange.cs new file mode 100644 index 00000000..e16c6d35 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/Core/BFastRange.cs @@ -0,0 +1,44 @@ +using System.IO; +using System.Runtime.InteropServices; + +namespace Vim.BFastLib.Core +{ + /// + /// This tells us where a particular array begins and ends in relation to the beginning of a file. + /// * Begin must be less than or equal to End. + /// * Begin must be greater than or equal to DataStart + /// * End must be less than or equal to DataEnd + /// + [StructLayout(LayoutKind.Explicit, Pack = 8, Size = 16)] + public struct BFastRange + { + [FieldOffset(0)] public long Begin; + [FieldOffset(8)] public long End; + + public long Count => End - Begin; + public static long Size = 16; + + public override bool Equals(object x) + => x is BFastRange other && Equals(other); + + public bool Equals(BFastRange other) + => Begin == other.Begin && End == other.End; + + public BFastRange OffsetBy(long offset) + => new BFastRange() + { + Begin = Begin + offset, + End = End + offset + }; + } + + public static class BFastRangeExtensions + { + public static BFastRange FullRange(this Stream stream) + => new BFastRange() + { + Begin = 0, + End = stream.Length + }; + } +} diff --git a/src/cs/bfast/Vim.BFast/Core/BFastSection.cs b/src/cs/bfast/Vim.BFast/Core/BFastSection.cs new file mode 100644 index 00000000..fbf40b5c --- /dev/null +++ b/src/cs/bfast/Vim.BFast/Core/BFastSection.cs @@ -0,0 +1,103 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace Vim.BFastLib.Core +{ + /// + /// Represents a section of the bfast that will be written to at some point. + /// + public class BFastSection + { + public readonly long Origin; + public readonly long LocalStart; + public readonly long Length; + public long AbsoluteStart => LocalStart + Origin; + + public long AbsoluteEnd => AbsoluteStart + Length; + public long LocalEnd => LocalStart + Length; + public long End => AbsoluteStart + Length; + public BFastRange LocalRange => new BFastRange() + { + Begin = LocalStart, + End = LocalEnd + }; + + public BFastSection(long start, long length, long origin = 0) + { + LocalStart = start; + Length = length; + Origin = origin; + } + + /// + /// Returns a new range offset by given amount. + /// + public BFastSection Offset(long offset) + { + return new BFastSection(AbsoluteStart, Length, offset); + } + + /// + /// Returns an equivalent section but with given origin. + /// + public BFastSection Rebase(long origin) + { + return new BFastSection(LocalStart - origin, Length, origin); + } + + /// + /// Returns a new range Starting where this one ends. + /// + /// Byte length of the section + public BFastSection Next(long length) + { + return new BFastSection(LocalEnd, length, Origin); + } + + /// + /// Writes 0 bytes over the whole section. + /// + public void Clear(Stream stream) + { + stream.Seek(AbsoluteStart, SeekOrigin.Begin); + for (var i = 0; i < Length; i++) + { + stream.WriteByte(0); + } + } + + /// + /// Writes given bytes in the section. Throws if bytes don't match section length. + /// + public void Write(Stream stream, byte[] bytes) + { + if (bytes.Length != Length) + throw new Exception("Data length not matching section length"); + + stream.Seek(AbsoluteStart, SeekOrigin.Begin); + stream.Write(bytes); + } + + /// + /// Writes given value in the section. Throws if value don't match section length. + /// + unsafe public void Write(Stream stream, T value) where T : unmanaged + { + Debug.Assert(sizeof(T) == Length); + stream.Seek(AbsoluteStart, SeekOrigin.Begin); + stream.WriteValue(value); + } + + /// + /// Writes given buffer and returns resulting section. + /// + public static BFastSection Write(Stream stream, IWritable buffer) + { + var start = stream.Position; + buffer.Write(stream); + return new BFastSection(start, stream.Position - start); + } + } +} + diff --git a/src/cs/bfast/Vim.BFast/Core/BFastStrings.cs b/src/cs/bfast/Vim.BFast/Core/BFastStrings.cs new file mode 100644 index 00000000..f2bc1e67 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/Core/BFastStrings.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Text; + +namespace Vim.BFastLib.Core +{ + public static class BFastStrings + { + /// + /// Converts a collection of strings, into a null-separated byte[] array + /// + public static byte[] Pack(IEnumerable strings) + { + var r = new List(); + foreach (var name in strings) + { + var bytes = Encoding.UTF8.GetBytes(name); + r.AddRange(bytes); + r.Add(0); + } + return r.ToArray(); + } + + + /// + /// Converts a byte[] array encoding a collection of strings separate by NULL into an array of string + /// + public static string[] Unpack(byte[] bytes) + { + var r = new List(); + if (bytes.Length == 0) + return r.ToArray(); + var prev = 0; + for (var i = 0; i < bytes.Length; ++i) + { + if (bytes[i] == 0) + { + r.Add(Encoding.UTF8.GetString(bytes, prev, i - prev)); + prev = i + 1; + } + } + if (prev < bytes.Length) + r.Add(Encoding.UTF8.GetString(bytes, prev, bytes.Length - prev)); + return r.ToArray(); + } + } +} diff --git a/src/cs/bfast/Vim.BFast/Core/BFastWriter.cs b/src/cs/bfast/Vim.BFast/Core/BFastWriter.cs new file mode 100644 index 00000000..f6b8455c --- /dev/null +++ b/src/cs/bfast/Vim.BFast/Core/BFastWriter.cs @@ -0,0 +1,100 @@ +using System.IO; + +namespace Vim.BFastLib.Core +{ + /// + /// Provide methods to write a buffer collection to a stream. + /// + public class BFastWriter + { + private readonly string[] _bufferNames; + private readonly IWritable[] _buffers; + private readonly byte[] _packedNames; + + private readonly BFastSection _preamble; + private readonly BFastSection _ranges; + private readonly BFastSection _names; + + public long Start => _preamble.AbsoluteStart; + + public BFastWriter(string[] names, IWritable[] buffers, long offset = 0) + { + if(names.Length != buffers.Length) + { + throw new System.Exception("Names and buffer length must match"); + } + + _bufferNames = names; + _buffers = buffers; + _packedNames = BFastStrings.Pack(names); + + _preamble = new BFastSection(0, 32).Offset(offset); + _ranges = _preamble.Next((buffers.Length + 1) * 16); + _names = _ranges.Next(_packedNames.Length); + } + + /// + /// Writes to given stream, which may or may not be at Position 0. + /// + public unsafe void Write(Stream stream) + { + var offset = stream.Position; + if (Start != stream.Position) + { + // Offset sections if stream not at 0 + Offset(stream.Position).Write(stream); + return; + } + + // Leave space for preamble + _preamble.Clear(stream); + + // Leave space for ranges and write Names range. + _ranges.Clear(stream); + WriteRange(stream, _ranges.AbsoluteStart, 0, _names.LocalRange); + + // Write Names + _names.Write(stream, _packedNames); + + // Write each buffer and go back to write its Range. + var dataPointer = _names.End; + for (var i = 0; i < _buffers.Length; i++) + { + var section = WriteBuffer(stream, dataPointer, _buffers[i]).Rebase(offset); + WriteRange(stream, _ranges.AbsoluteStart, i + 1, section.LocalRange); + dataPointer = section.End; + } + + // Finally go back to write the preamble. + var preamble = new BFastPreamble() + { + Magic = BFastConstants.Magic, + NumArrays = _buffers.Length + 1, + DataStart = _ranges.End - offset, + DataEnd = dataPointer - offset, + }; + _preamble.Write(stream, preamble); + + // Move pointer back to end as the caller would expect + stream.Seek(dataPointer, SeekOrigin.Begin); + } + + private BFastWriter Offset(long offset) + { + return new BFastWriter(_bufferNames, _buffers, offset); + } + + private void WriteRange(Stream stream, long start, int index, BFastRange range) + { + stream.Seek(start + index * 16, SeekOrigin.Begin); + stream.WriteValue(range); + } + + private BFastSection WriteBuffer(Stream stream, long start, IWritable buffer) + { + stream.Seek(start, SeekOrigin.Begin); + return BFastSection.Write(stream, buffer); + } + } +} + diff --git a/src/cs/bfast/Vim.BFast/SeekContext.cs b/src/cs/bfast/Vim.BFast/SeekContext.cs deleted file mode 100644 index eea7102e..00000000 --- a/src/cs/bfast/Vim.BFast/SeekContext.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.IO; - -namespace Vim.BFast -{ - /// - /// Manages a Stream's seek pointer within a given `using` scope. - /// When the stream context is disposed, the seek position is reset - /// to the original position when the object was created. - /// - public sealed class SeekContext : IDisposable - { - /// - /// The seekable stream. - /// - public readonly Stream Stream; - - /// - /// The original stream seek position when the object was created. - /// - public readonly long OriginalSeekPosition; - - /// - /// Constructor. - /// - public SeekContext(Stream stream) - { - if (!stream.CanSeek) - throw new ArgumentException("Stream must be seekable."); - - Stream = stream; - OriginalSeekPosition = stream.Position; - } - - /// - /// Disposer. - /// - public void Dispose() - => Stream.Seek(OriginalSeekPosition, SeekOrigin.Begin); - } -} diff --git a/src/cs/bfast/Vim.BFast/Unsafe/MemStreamHelpers.cs b/src/cs/bfast/Vim.BFast/Unsafe/MemStreamHelpers.cs new file mode 100644 index 00000000..40c43e76 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/Unsafe/MemStreamHelpers.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.IO; + +namespace Vim.BFastLib.Core +{ + /// + /// This class would benefit from being in a generic utilities class, however, having it here allows BFAST to be a standalone without dependencies. + /// + public static class MemStreamHelpers + { + /// + /// Creates and fills a new Memory Stream from the given array. + /// The stream is returned at Position 0. + /// + public static unsafe MemoryStream ToMemoryStream(this T[] array) where T : unmanaged + { + var mem = new MemoryStream(); + mem.Write(array); + mem.Seek(0, SeekOrigin.Begin); + return mem; + } + + /// + /// Creates and fills a new Memory Stream from the given array. + /// The stream is returned at Position 0. + /// + public static unsafe MemoryStream ToMemoryStream(this IEnumerable enumerable) where T : unmanaged + { + var mem = new MemoryStream(); + foreach(var e in enumerable) + { + mem.WriteValue(e); + } + mem.Seek(0, SeekOrigin.Begin); + return mem; + } + } +} diff --git a/src/cs/bfast/Vim.BFast/Unsafe/UnsafeCast.cs b/src/cs/bfast/Vim.BFast/Unsafe/UnsafeCast.cs new file mode 100644 index 00000000..8ea2e41b --- /dev/null +++ b/src/cs/bfast/Vim.BFast/Unsafe/UnsafeCast.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using System.IO; + +namespace Vim.BFastLib.Core +{ + public static class UnsafeCast + { + /// + /// Cast an array of type TInput to an array of type TOutput + /// This is not a Cast but an actual byte level conversion. + /// + public static unsafe TResult[] Cast(this TInput[] array) + where TInput : unmanaged + where TResult : unmanaged + { + var count = array.Length * (sizeof(TInput) / sizeof(TResult)); + using (var mem = array.ToMemoryStream()) + { + return mem.ReadArray(count); + } + } + + /// + /// Converts an enumerable of type TInput to an enumerable of type TOutput + /// This is not a Cast but an actual byte level conversion. + /// + public static IEnumerable Cast(this IEnumerable input, int chunksize = 1048576) + where TInput : unmanaged + where TResult : unmanaged + { + var stream = new MemoryStream(); + var array = new TResult[chunksize]; + var chunks = UnsafeHelpers.Chunkify(input, chunksize); + while (chunks.MoveNext()) + { + (var chunk, var size) = chunks.Current; + stream.Seek(0, SeekOrigin.Begin); + stream.Write(chunk, size); + var count = ReadArray(stream, array); + + if (count > 0) + { + for (var i = 0; i < count; i++) + { + yield return array[i]; + } + } + } + } + + // Function is extracted because unsafe code cannot appear in generator + private static unsafe int ReadArray(MemoryStream stream, T[] array) where T : unmanaged + { + var length = (int)stream.Position; + if (length < sizeof(T)) + { + return 0; + } + + var count = length / sizeof(T); + stream.Seek(0, SeekOrigin.Begin); + stream.ReadArray(array, count); + return count; + } + } +} diff --git a/src/cs/bfast/Vim.BFast/Unsafe/UnsafeHelpers.cs b/src/cs/bfast/Vim.BFast/Unsafe/UnsafeHelpers.cs new file mode 100644 index 00000000..dd3d3521 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/Unsafe/UnsafeHelpers.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Vim.BFastLib.Core +{ + public static class UnsafeHelpers + { + /// + /// Returns an enumeration of chunks of the given size from the given enumeration. + /// + public static IEnumerator<(T[], int)> Chunkify(IEnumerable source, int chunkSize = 1048576) + { + var chunk = new T[chunkSize]; + var index = 0; + + foreach (var item in source) + { + chunk[index++] = item; + + if (index == chunkSize) + { + yield return (chunk, index); + index = 0; + } + } + + if (index > 0) + { + yield return (chunk, index); + } + } + + /// + /// Copies given number of bytes from input stream to output stream. Optional buffer size. + /// + public static void CopySome(this Stream input, Stream output, int bytes, int bufferSize = 32768) + { + var buffer = new byte[bufferSize]; + int read; + while (bytes > 0 && + (read = input.Read(buffer, 0, Math.Min(buffer.Length, bytes))) > 0) + { + output.Write(buffer, 0, read); + bytes -= read; + } + } + } +} diff --git a/src/cs/bfast/Vim.BFast/Unsafe/UnsafeReadArray.cs b/src/cs/bfast/Vim.BFast/Unsafe/UnsafeReadArray.cs new file mode 100644 index 00000000..5d3cac1d --- /dev/null +++ b/src/cs/bfast/Vim.BFast/Unsafe/UnsafeReadArray.cs @@ -0,0 +1,93 @@ +using System; +using System.IO; + +namespace Vim.BFastLib.Core +{ + public static class UnsafeReadArray + { + /// + /// Reads and converts the next value of the stream. + /// + public static unsafe T ReadValue(this Stream stream) where T : unmanaged + { + T r; + stream.ReadBytesBuffered((byte*)&r, sizeof(T)); + return r; + } + + /// + /// Reads bytes until the end of the stream and converts them to T. + /// + public static unsafe T[] ReadArray(this Stream stream) where T : unmanaged + { + return ReadArrayBytes(stream, stream.Length - stream.Position); + } + + /// + /// Reads and converts the next ByteCount bytes from the stream and returns the result as a new array. + /// Will throw if ByteCount is not a multiple of sizeof T. + /// + public static unsafe T[] ReadArrayBytes(this Stream stream, long byteCount) where T : unmanaged + { + var count = byteCount / sizeof(T); + if (byteCount % sizeof(T) != 0) + throw new Exception($"The number of bytes {byteCount} is not divisible by the size of the type {sizeof(T)}"); + if (count >= int.MaxValue) + throw new Exception($"{count} exceeds the maximum number of items that can be read into an array {int.MaxValue}"); + return ReadArray(stream, (int)count); + } + + /// + /// Reads and converts the next Count value from the stream and returns the result as a new array. + /// + public static unsafe T[] ReadArray(this Stream stream, int count) where T : unmanaged + { + var r = new T[count]; + fixed (T* pDest = r) + { + var pBytes = (byte*)pDest; + stream.ReadBytesBuffered(pBytes, (long)count * sizeof(T)); + } + return r; + } + + /// + /// Reads and converts the next Count values from the stream and writes the result into the given array. + /// + public static unsafe void ReadArray(this Stream stream, T[] array, int count) where T : unmanaged + { + if (array.Length < count) + throw new Exception("Destination array needs to be larger than count."); + + fixed (T* pDest = array) + { + var pBytes = (byte*)pDest; + stream.ReadBytesBuffered(pBytes, (long)count * sizeof(T)); + } + } + + /// + /// Helper for reading arrays of arbitrary unmanaged types from a Stream, that might be over 2GB of size. + /// That said, in C#, you can never load more int.MaxValue numbers of items. + /// NOTE: Arrays are still limited to 2gb in size unless gcAllowVeryLargeObjects is set to true + /// in the runtime environment. + /// https://docs.microsoft.com/en-us/dotnet/api/system.array?redirectedfrom=MSDN&view=netframework-4.7.2#remarks + /// Alternatively, we could convert to .Net Core + /// + private static unsafe void ReadBytesBuffered(this Stream stream, byte* dest, long count, int bufferSize = 4096) + { + var buffer = new byte[bufferSize]; + int bytesRead; + fixed (byte* pBuffer = buffer) + { + while ((bytesRead = stream.Read(buffer, 0, (int)Math.Min(buffer.Length, count))) > 0) + { + if (dest != null) + Buffer.MemoryCopy(pBuffer, dest, count, bytesRead); + count -= bytesRead; + dest += bytesRead; + } + } + } + } +} diff --git a/src/cs/bfast/Vim.BFast/Unsafe/UnsafeReadEnumerable.cs b/src/cs/bfast/Vim.BFast/Unsafe/UnsafeReadEnumerable.cs new file mode 100644 index 00000000..2a4a1652 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/Unsafe/UnsafeReadEnumerable.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Vim.BFastLib.Core +{ + public static class UnsafeReadEnumerable + { + /// + /// Reads the next byteLength bytes from the stream and return the result as an enumerable of T + /// Throws if byteLength is not a multiple of T size. + /// + public static IEnumerable ReadEnumerableByte(this Stream stream, long byteLength, int bufferSize = 4096) where T : unmanaged + { + var count = GetTCount(byteLength); + return ReadEnumerable(stream, count, bufferSize); + } + + /// + /// Reads the next count values of T from the stream as an enumerable. + /// + public static IEnumerable ReadEnumerable(this Stream stream, long count, int bufferSize = 4096) where T : unmanaged + { + var remaining = count; + var (array, buffer) = AllocBuffers(bufferSize); + + while (remaining > 0) + { + var toRead = (int)Math.Min(bufferSize, remaining); + var read = FillArray(stream, toRead, array, buffer); + + for (var i = 0; i < read; i++) + { + yield return array[i]; + } + remaining -= read; + } + } + + // Function is extracted because unsafe code cannot appear in generator + private static unsafe long GetTCount(long byteLength) where T : unmanaged + { + if (byteLength % sizeof(T) != 0) + { + throw new Exception("Byte length must be a multiple of T size."); + } + return byteLength / sizeof(T); + } + + // Function is extracted because unsafe code cannot appear in generator + private static unsafe (T[], byte[]) AllocBuffers(int count) where T : unmanaged + { + return (new T[count], new byte[count * sizeof(T)]); + } + + // Function is extracted because unsafe code cannot appear in generator + private static unsafe int FillArray(Stream stream, int count, T[] array, byte[] buffer) where T : unmanaged + { + fixed (T* pDestTyped = array) + fixed (byte* pBuffer = buffer) + { + var pDestBytes = (byte*)pDestTyped; + var toRead = Math.Min(buffer.Length, count * sizeof(T)); + var bytesRead = stream.Read(buffer, 0, toRead); + Buffer.MemoryCopy(pBuffer, pDestTyped, array.Length * sizeof(T), bytesRead); + return bytesRead / sizeof(T); + } + } + } +} diff --git a/src/cs/bfast/Vim.BFast/Unsafe/UnsafeWrite.cs b/src/cs/bfast/Vim.BFast/Unsafe/UnsafeWrite.cs new file mode 100644 index 00000000..a3d75978 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/Unsafe/UnsafeWrite.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Vim.BFastLib.Core +{ + public static class UnsafeWrite + { + /// + /// Converts given value to bytes and writes resulting bytes to the stream + /// + public static unsafe void WriteValue(this Stream stream, T x) where T : unmanaged + { + var p = &x; + stream.WriteBytesBuffered((byte*)p, sizeof(T)); + } + + /// + /// Converts values of the given array to bytes and writes the resulting bytes to the stream. + /// + public static unsafe void Write(this Stream stream, T[] xs) where T : unmanaged + { + Write(stream, xs, xs.LongLength); + } + + /// + /// Converts the first Count elements of an array to bytes and writes the resulting bytes to the stream. + /// + public static unsafe void Write(this Stream stream, T[] xs, long count) where T : unmanaged + { + fixed (T* p = xs) + { + stream.WriteBytesBuffered((byte*)p, count * sizeof(T)); + } + } + + /// + /// Converts and writes the elements of values to the given stream. + /// + public static unsafe void Write(this Stream stream, IEnumerable values) where T : unmanaged + { + var chunks = UnsafeHelpers.Chunkify(values); + while (chunks.MoveNext()) + { + var (chunk, size) = chunks.Current; + fixed (T* p = chunk) + { + stream.WriteBytesBuffered((byte*)p, size * sizeof(T)); + } + } + } + + /// + /// Writes an arbitrary large numbers of bytes to the stream. + /// + private static unsafe void WriteBytesBuffered(this Stream stream, byte* src, long count, int bufferSize = 4096) + { + var buffer = new byte[bufferSize]; + fixed (byte* pBuffer = buffer) + { + while (count > 0) + { + var toWrite = (int)Math.Min(count, buffer.Length); + Buffer.MemoryCopy(src, pBuffer, buffer.Length, toWrite); + stream.Write(buffer, 0, toWrite); + count -= toWrite; + src += toWrite; + } + } + } + } +} diff --git a/src/cs/bfast/Vim.BFast/UnsafeHelpers.cs b/src/cs/bfast/Vim.BFast/UnsafeHelpers.cs deleted file mode 100644 index 7d01a1de..00000000 --- a/src/cs/bfast/Vim.BFast/UnsafeHelpers.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.IO; - -namespace Vim.BFast -{ - /// - /// This class would benefit from being in a generic utilities class, however, having it here allows BFAST to be a standalone without dependencies. - /// - public static class UnsafeHelpers - { - /// - /// Helper for reading arbitrary unmanaged types from a Stream. - /// - public static unsafe void ReadBytesBuffered(this Stream stream, byte* dest, long count, int bufferSize = 4096) - { - var buffer = new byte[bufferSize]; - int bytesRead; - fixed (byte* pBuffer = buffer) - { - while ((bytesRead = stream.Read(buffer, 0, (int)Math.Min(buffer.Length, count))) > 0) - { - if (dest != null) - Buffer.MemoryCopy(pBuffer, dest, count, bytesRead); - count -= bytesRead; - dest += bytesRead; - } - } - } - - /// - /// Helper for writing arbitrary large numbers of bytes - /// - public static unsafe void WriteBytesBuffered(this Stream stream, byte* src, long count, int bufferSize = 4096) - { - var buffer = new byte[bufferSize]; - fixed (byte* pBuffer = buffer) - { - while (count > 0) - { - var toWrite = (int)Math.Min(count, buffer.Length); - Buffer.MemoryCopy(src, pBuffer, buffer.Length, toWrite); - stream.Write(buffer, 0, toWrite); - count -= toWrite; - src += toWrite; - } - } - } - - /// - /// Helper for reading arbitrary unmanaged types from a Stream. - /// - public static unsafe void Read(this Stream stream, T* dest) where T : unmanaged - => stream.ReadBytesBuffered((byte*)dest, sizeof(T)); - - /// - /// Helper for reading arrays of arbitrary unmanaged types from a Stream, that might be over 2GB of size. - /// That said, in C#, you can never load more int.MaxValue numbers of items. - /// NOTE: Arrays are still limited to 2gb in size unless gcAllowVeryLargeObjects is set to true - /// in the runtime environment. - /// https://docs.microsoft.com/en-us/dotnet/api/system.array?redirectedfrom=MSDN&view=netframework-4.7.2#remarks - /// Alternatively, we could convert to .Net Core - /// - public static unsafe T[] ReadArray(this Stream stream, int count) where T : unmanaged - { - var r = new T[count]; - fixed (T* pDest = r) - { - - var pBytes = (byte*)pDest; - stream.ReadBytesBuffered(pBytes, (long)count * sizeof(T)); - } - return r; - } - - /// - /// A wrapper for stream.Seek(numBytes, SeekOrigin.Current) to avoid allocating memory for unrecognized buffers. - /// - public static void SkipBytes(this Stream stream, long numBytes) - => stream.Seek(numBytes, SeekOrigin.Current); - - /// - /// Helper for reading arrays of arbitrary unmanaged types from a Stream, that might be over 2GB of size. - /// That said, in C#, you can never load more int.MaxValue numbers of items. - /// - public static unsafe T[] ReadArrayFromNumberOfBytes(this Stream stream, long numBytes) where T : unmanaged - { - var count = numBytes / sizeof(T); - if (numBytes % sizeof(T) != 0) - throw new Exception($"The number of bytes {numBytes} is not divisible by the size of the type {sizeof(T)}"); - if (count >= int.MaxValue) - throw new Exception($"{count} exceeds the maximum number of items that can be read into an array {int.MaxValue}"); - return stream.ReadArray((int)count); - } - - /// - /// Helper for writing arbitrary unmanaged types - /// - public static unsafe void WriteValue(this Stream stream, T x) where T : unmanaged - { - var p = &x; - stream.WriteBytesBuffered((byte*)p, sizeof(T)); - } - - - /// - /// Helper for writing arrays of unmanaged types - /// - public static unsafe void Write(this Stream stream, T[] xs) where T : unmanaged - { - fixed (T* p = xs) - { - stream.WriteBytesBuffered((byte*)p, xs.LongLength * sizeof(T)); - } - } - } -} diff --git a/src/cs/bfast/Vim.BFast/Vim.BFast.csproj b/src/cs/bfast/Vim.BFast/Vim.BFast.csproj index 3c6ef62c..16404976 100644 --- a/src/cs/bfast/Vim.BFast/Vim.BFast.csproj +++ b/src/cs/bfast/Vim.BFast/Vim.BFast.csproj @@ -2,36 +2,15 @@ netstandard2.0 - Vim.BFast - https://github.com/vimaec/bfast - https://github.com/vimaec/bfast - GitHub - true - license.txt - BFAST is a library for converting collections of named binary buffers to a single byte array for efficient cross-platform serialization and deserialization. - 1.5.0.0 - 1.5.0.0 - 1.5.0 true - true - true - true - snupkg - - - - - true - - - - + True + diff --git a/src/cs/bfast/Vim.BFast/readme.md b/src/cs/bfast/Vim.BFast/readme.md deleted file mode 100644 index 4525dbd1..00000000 --- a/src/cs/bfast/Vim.BFast/readme.md +++ /dev/null @@ -1,76 +0,0 @@ -# BFAST - -[](https://www.nuget.org/packages/Vim.Bfast) - -BFAST stands for the **B**inary **F**ormat for **A**rray **S**erialization and **T**ransmission. - -## Summary - -BFAST is an extremely efficent and simple alternative to ProtoBuf and FlatBuffers, whe data that follows the form -of a collection of name/value pairs where names are strings, and values are arrays of bytes. - -* Unlike JSON, XML, and YAML: BFAST is binary -* Unlike ProtoBuf and FlatBuffers: BFAST does not require a schema -* Unlike TAR: BFAST is simple and easy to implement -* Unlike ZIP: BFAST is not concerned with compression - -## Details - -BFAST is a data format for simple and efficient serialization and deserialization of -collections of named data buffers in a generic and cross-platform manner. -A BFAST data buffer is a named arrays of binary data (bytes) that is aligned on 64 byte boundaries. - -You would use tshe BFAST structure if you have a binary data to serialize that is mostly in the form of -long arrays. For example a set of files that you want to bundle together without wanting to bring in -the overhead of a compression library or re-implementing TAR. We use BFAST to encode mesh data and as -containers for other data. - -BFAST is intended to be a high-performance implementation that is fast enough to use as a purely -in-memory low-level data format, for representing arbitrary data such as meshes, point-clouds, image data, -collections of files, etc. and to scale to data that must be processed out of core. One of the design goals was to assure -that the format could be easily and efficiently decoded using JavaScript on most modern web-browsers -with very little code. - -BFAST is maintained by [VIMaec LLC](https://www.vimaec.com) and is licensed under the terms of -the MIT License. - -## Features - -* Very small implementation overhead -* Easy to implement efficient and conformant encoders and decoders in different languages -* Fast random access to any point in the data format with a minimum of disk accesses -* Format and endianess easily identified through a magic number at the front of the file -* Data arrays are 64 byte aligned to facilitate casting to SIMD data types (eg. AVX-512) -* Array offsets are encoded using 64-bit integers to supports large data sets -* Positions of data buffers are encoded in the beginning of the file -* Quick and easy to validate that a block is a valid BFAST encoding of data - -## Rationale - -Encoding containers of binary data is a deceptively simple problem that is easy to solve -in ways that have are not as efficient of generic as possible, or dependent on a particular platform. -We proposing a standardized solution to the problem in the form of a specification and sample -implementation that can allow software to easily encode low level binary data in a manner -that is both efficient and cross-platform. - -## Related Libraries - -The following is a partial list of commonly used binary data serialization formats: - -* [Protcol Buffers](https://developers.google.com/protocol-buffers/) -* [FlatBuffers](https://github.com/google/flatbuffers) -* [BINN](https://github.com/liteserver/binn/) -* [BSON](http://bsonspec.org/) -* [UBJSON](http://ubjson.org/) -* [MessagePack](https://msgpack.org/) -* [CBOR](https://cbor.io/) -* [TAR](https://www.gnu.org/software/tar/manual/html_node/Standard.html) - -For a more comprehensive list see: - -* https://en.wikipedia.org/wiki/Comparison_of_data-serialization_formats -* https://en.wikipedia.org/wiki/List_of_archive_formats - -# Specification - -See the file [spec.txt](spec.txt) for the official specification. diff --git a/src/cs/bfast/Vim.BFast/spec.txt b/src/cs/bfast/Vim.BFast/spec.txt deleted file mode 100644 index 1e432cf0..00000000 --- a/src/cs/bfast/Vim.BFast/spec.txt +++ /dev/null @@ -1,100 +0,0 @@ -BFAST Specification Proposal -June 23rd, 2020 - -Summary - -BFAST is a simple high-performance general purpose method of packing and unpacking named -arrays of binary data for serialization, deserialization, and transport in a cross-platform -and language agnostic manner. - -Introduction - -Of the myriad standardized formats for binary representation of structured data most -are very general and support nested data and schemas. - -One of the most common use case for structured binary data are collections of key/value -pairs where each key is a string and the value is an array of bytes. -An example of this are archive file formats like TAR and ZIP. Many 3D and image formats -data also follow this format. - -When data conforms to this kind of schema, then most binary formats (e.g CBOR, FlatBuffers, -ProtoBuf, Capnproto, etc.) introduce more overhead than is required, in terms of -memory, performance, and code. - -Often developers encountering this scenario will write an ad-hoc serialization/deserialization -protocol, which can lead to some problems: - - * Endianness may or may not be considered - * When memory mapping the structure, O(1) random access to buffers might not possible - * Programs written by other developers cannot easily deduce the layout of the format - * Alignment of the buffers might not be suitable for fast processing of the data arrays - * Testing and validation of the protocol might not be suitably robust - -The BFAST format is intended to provide a simple open standard that developers can use instead of -rolling their own. We have used in production code in a multitude of scenarios on a wide range of -devices and languages, and found it to be quite satsifactory in terms of efficiency and simplicity. - -Like TAR, BFAST is explicitly not a compression format, and can be easily compressed using -any compression algorithm. It is appropriate for use as an archive format, or as a container -format for other more complex file formats that provide additional semantic requirements -on the usage and naming of specific buffers. - -Features - - * The file format can be quickly detected reading the first 8 bytes - * Endianness of the writer can be detected from first 8 bytes, and compensated for by a reader - * Buffers names are stored in the first buffer, and can be quickly retrieved - * Each data-buffer is aligned on 64 bytes (for easy SIMD register alignment) - * Buffer begin/end positions are stored in the beginning of the file for fast seeking to data - * Buffer names can be arbitrarily long sequences of Utf-8 characters - * Buffers can be over 2GB in length - -Header Layout - - Preamble (bytes 0..32) - int64_t Magic; - int64_t DataStart; // The beginning position of the first data buffer, 64 byte aligned - int64_t DataEnd; // The end position of the last data buffer - int64_t Count; // Number of all buffers, including name buffer. There should always be at least 1 buffer. - - Ranges (bytes 32..32 + NumArray*16) - int64_t Begin; - int64_t End; - -Requirements - - * The first eight bytes are 0xBFA5 - * The file can be encoded as big-endian or little-endian - * If the endianness of the reader is different then the writer, the eight bytes will apprea as 0xA5BF << 48; - * Each DataBuffer starts on a 64 byte aligned buffer - * Buffer names are stored as null terminated Utf8 strings in the first buffer - * There are always exactly n-1 buffer names, where n is the number of buffers - * Buffer names can be empty (0 length strings) - * Multiple buffers can have the same name - * There is no padding between each range structure - * There is no padding between the header and the range - * The range struct is 16 bytes long - * The header struct is 32 bytes - * A data buffer could be empty, in which case, the begin and end is the same - * If a data buffer is empty, the the next data buffer will point to the data buffer beginning - * The DataStart can be computed by align(64, sizeof(Header) + sizeof(Range) * Header.NumArrays) - * Header.DataStart is equivalent to Range[0].Begin - * Header.DataEnd is equivalent to Range[N-1].End - -Related Information - - * [Zip]https://en.wikipedia.org/wiki/Zip_(file_format) - * [Protcol Buffers](https://developers.google.com/protocol-buffers/) - * [FlatBuffers](https://github.com/google/flatbuffers) - * [BINN](https://github.com/liteserver/binn/) - * [BSON](http://bsonspec.org/) - * [UBJSON](http://ubjson.org/) - * [MessagePack](https://msgpack.org/) - * [CBOR](https://cbor.io/) - * [TAR](https://www.gnu.org/software/tar/manual/html_node/Standard.html) - -For a more comprehensive list see: - - * [Comparison of Data Serialization Formats](https://en.wikipedia.org/wiki/Comparison_of_data-serialization_formats) - * [List of Archive Formats](https://en.wikipedia.org/wiki/List_of_archive_formats) - diff --git a/src/cs/g3d/Vim.G3d.AssimpWrapper/AssimpExtensions.cs b/src/cs/g3d/Vim.G3d.AssimpWrapper/AssimpExtensions.cs deleted file mode 100644 index 80e26239..00000000 --- a/src/cs/g3d/Vim.G3d.AssimpWrapper/AssimpExtensions.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using System.Linq; -using Assimp; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.G3d.AssimpWrapper -{ - public static class AssimpExtensions - { - public static Vector2 ToMath3D(this Vector2D v) - => new Vector2(v.X, v.Y); - - public static Vector3 ToMath3D(this Vector3D v) - => new Vector3(v.X, v.Y, v.Z); - - public static Vector3 ToMath3D(this Color3D v) - => new Vector3(v.R, v.G, v.B); - - public static Vector4 ToMath3D(this Color4D v) - => new Vector4(v.R, v.G, v.B, v.A); - - public static Math3d.Matrix4x4 ToMath3D(this Assimp.Matrix4x4 m) - => new Math3d.Matrix4x4( - m.A1, m.A2, m.A3, m.A4, - m.B1, m.B2, m.B3, m.B4, - m.C1, m.C2, m.C3, m.C4, - m.D1, m.D2, m.D3, m.D4); - - public static bool IsTriangular(this Mesh mesh) - => mesh.Faces.All(f => f.IndexCount == 3); - - public static G3D ToG3d(this Scene scene) - { - var meshes = scene.Meshes.Select(m => m.ToG3D()).ToIArray(); - var nodes = scene.GetNodes().ToIArray(); - if (nodes.Count == 0 || nodes.Count == 1) - return meshes.Count > 0 ? meshes[0] : G3D.Empty; - - var mergedAttributes = meshes.Merge().Attributes.ToList(); - - var subGeoTransforms = nodes.Select(n => n.Transform.ToMath3D()).ToInstanceTransformAttribute(); - mergedAttributes.Add(subGeoTransforms); - - var meshIndices = nodes.Select(n => n.MeshIndex).ToInstanceMeshAttribute(); - mergedAttributes.Add(meshIndices); - - return mergedAttributes.ToG3d(); - } - - public static G3D ToG3D(this Mesh mesh) - { - // The Assimp mesh must be triangulated - if (mesh.FaceCount == 0) - return G3D.Empty; - - var bldr = new G3DBuilder(); - - // Note: should be at most 3 for meses, but could 2 for lines, or 1 for point clouds - var numCornersPerFace = mesh.Faces[0].IndexCount; - if (numCornersPerFace > 3) - throw new Exception("The Assimp mesh must be triangulated as a post-process"); - if (numCornersPerFace <= 0) - throw new Exception("The Assimp mesh has faces without indices"); - foreach (var f in mesh.Faces) - { - if (f.IndexCount != numCornersPerFace) - throw new Exception($"Each face of the assimp mesh must have {numCornersPerFace} corners, but found one with {f.IndexCount}"); - } - bldr.SetObjectFaceSize(numCornersPerFace); - - var indices = mesh.GetIndices(); - if (indices.Length % numCornersPerFace != 0) - throw new Exception($"The mesh index buffer length {indices.Length} is not divisible by {numCornersPerFace}"); - - bldr.AddVertices(mesh.Vertices.ToIArray().Select(ToMath3D)); - bldr.AddIndices(indices); - - if (mesh.HasTangentBasis) - bldr.Add(mesh.BiTangents.ToIArray().Select(ToMath3D).ToVertexBitangentAttribute()); - - if (mesh.HasTangentBasis) - bldr.Add(mesh.Tangents.ToIArray().Select(x => ToMath3D(x).ToVector4()).ToVertexTangentAttribute()); - - if (mesh.HasNormals) - bldr.Add(mesh.Normals.ToIArray().Select(ToMath3D).ToVertexNormalAttribute()); - - for (var i = 0; i < mesh.TextureCoordinateChannelCount; ++i) - { - var uvChannel = mesh.TextureCoordinateChannels[i]; - bldr.Add(uvChannel.ToIArray().Select(ToMath3D).ToVertexUvwAttribute(i)); - } - - for (var i = 0; i < mesh.VertexColorChannelCount; ++i) - { - var vcChannel = mesh.VertexColorChannels[i]; - bldr.Add(vcChannel.ToIArray().Select(ToMath3D).ToVertexColorAttribute(i)); - } - - return bldr.ToG3D(); - } - } -} diff --git a/src/cs/g3d/Vim.G3d.AssimpWrapper/AssimpLoader.cs b/src/cs/g3d/Vim.G3d.AssimpWrapper/AssimpLoader.cs deleted file mode 100644 index bb0387be..00000000 --- a/src/cs/g3d/Vim.G3d.AssimpWrapper/AssimpLoader.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Assimp; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Vim.LinqArray; - -namespace Vim.G3d.AssimpWrapper -{ - public static class AssimpLoader - { - public static AssimpContext Context = new AssimpContext(); - - public class AssimpNode - { - public int MeshIndex { get; } - public Matrix4x4 Transform { get; } - public AssimpNode(int index, Matrix4x4 transform) - => (MeshIndex, Transform) = (index, transform); - } - - public static IEnumerable GetNodes(this Scene scene) - => scene == null || scene.RootNode == null - ? Enumerable.Empty() - : GetNodes(scene, scene.RootNode, scene.RootNode.Transform); - - public static IEnumerable GetNodes(this Scene scene, Node node, Matrix4x4 transform) - => node.MeshIndices.Select(idx => new AssimpNode(idx, node.Transform)) - .Concat(node.Children.SelectMany(c => GetNodes(scene, c, transform * c.Transform))); - - public static Scene Load(string filePath, bool triangulate = true) - => Context.ImportFile(filePath, triangulate ? PostProcessSteps.Triangulate : PostProcessSteps.None); - - public static bool CanLoad(string filePath) - => Context.IsImportFormatSupported(Path.GetExtension(filePath)); - } -} diff --git a/src/cs/g3d/Vim.G3d.AssimpWrapper/Vim.G3d.AssimpWrapper.csproj b/src/cs/g3d/Vim.G3d.AssimpWrapper/Vim.G3d.AssimpWrapper.csproj deleted file mode 100644 index afd6d77d..00000000 --- a/src/cs/g3d/Vim.G3d.AssimpWrapper/Vim.G3d.AssimpWrapper.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - netstandard2.0 - Vim.G3d.AssimpWrapper - - - - - - - - - - - diff --git a/src/cs/g3d/Vim.G3d.CodeGen/CodeBuilder.cs b/src/cs/g3d/Vim.G3d.CodeGen/CodeBuilder.cs new file mode 100644 index 00000000..41013d8f --- /dev/null +++ b/src/cs/g3d/Vim.G3d.CodeGen/CodeBuilder.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Vim.G3d.CodeGen +{ + public class CodeBuilder + { + private int _indentCount; + private StringBuilder _sb = new StringBuilder(); + + public CodeBuilder AppendRaw(string line) + { + _sb.Append(new string(' ', _indentCount * 4)); + _sb.AppendLine(line); + return this; + } + + public CodeBuilder AppendLines(IEnumerable lines) + { + foreach(var l in lines) { + AppendLine(l); + } + return this; + } + + public CodeBuilder AppendLine(string line = "") + { + var openBraces = line.Count(c => c == '{'); + var closeBraces = line.Count(c => c == '}'); + + // Sometimes we have {} on the same line + if (openBraces == closeBraces) + { + openBraces = 0; + closeBraces = 0; + } + + _indentCount -= closeBraces; + _sb.Append(new string(' ', _indentCount * 4)); + _sb.AppendLine(line); + _indentCount += openBraces; + return this; + } + + public void Indent() + { + ++_indentCount; + } + + public void Unindent() + { + _indentCount = System.Math.Max(0, --_indentCount); + } + + public void IndentOneLine(string line) + { + Indent(); + AppendLine(line); + Unindent(); + } + + public void UnindentOneLine(string line) + { + Unindent(); + AppendLine(line); + Indent(); + } + + public override string ToString() + => _sb.ToString(); + } +} diff --git a/src/cs/g3d/Vim.G3d.CodeGen/Definitions.cs b/src/cs/g3d/Vim.G3d.CodeGen/Definitions.cs new file mode 100644 index 00000000..8d53e40b --- /dev/null +++ b/src/cs/g3d/Vim.G3d.CodeGen/Definitions.cs @@ -0,0 +1,62 @@ +using Vim.Math3d; + +namespace Vim.G3d.CodeGen +{ + internal class Definitions + { + public static G3dEntity[] GetEntities() + { + return new G3dEntity[] { vim, mesh, materials, scene }; + } + + public static G3dEntity vim = new G3dEntity("G3dVim") + .Index("Indices", "g3d:corner:index:0:int32:1", "Positions") + .Data("Positions", "g3d:vertex:position:0:float32:3") + .Data("InstanceTransforms", "g3d:instance:transform:0:float32:16") + .Index("InstanceParents", "g3d:instance:parent:0:int32:1", "InstanceTransforms") + .Data("InstanceFlags", "g3d:instance:flags:0:uint16:1") + .Index("InstanceMeshes", "g3d:instance:mesh:0:int32:1", "MeshSubmeshOffsets") + .Index("MeshSubmeshOffsets", "g3d:mesh:submeshoffset:0:int32:1", "SubmeshIndexOffsets") + .Index("SubmeshIndexOffsets", "g3d:submesh:indexoffset:0:int32:1", "Indices") + .Index("SubmeshMaterials", "g3d:submesh:material:0:int32:1", "MaterialColors") + .Data("MaterialColors", "g3d:material:color:0:float32:4") + .Data("MaterialGlossiness", "g3d:material:glossiness:0:float32:1") + .Data("MaterialSmoothness", "g3d:material:smoothness:0:float32:1") + .Data("ShapeVertices", "g3d:shapevertex:position:0:float32:3") + .Index("ShapeVertexOffsets", "g3d:shape:vertexoffset:0:int32:1", "ShapeVertices") + .Data("ShapeColors", "g3d:shape:color:0:float32:4") + .Data("ShapeWidths", "g3d:shape:width:0:float32:1"); + + public static G3dEntity scene = new G3dEntity("G3dScene") + .Data("ChunkCount", "g3d:chunk:count:0:int32:1") + .Data("InstanceMeshes", "g3d:instance:mesh:0:int32:1") + .Data("InstanceTransformData", "g3d:instance:transform:0:float32:16") + .Data("InstanceNodes", "g3d:instance:node:0:int32:1") + .Data("InstanceGroups", "g3d:instance:group:0:int32:1") + .Data("InstanceTags", "g3d:instance:tag:0:int64:1") + .Data("InstanceFlags", "g3d:instance:flags:0:uint16:1") + .Data("InstanceMins", "g3d:instance:min:0:float32:3") + .Data("InstanceMaxs", "g3d:instance:max:0:float32:3") + .Data("MeshChunks", "g3d:mesh:chunk:0:int32:1") + .Data("MeshChunkIndices", "g3d:mesh:chunkindex:0:int32:1") + .Data("MeshVertexCounts", "g3d:mesh:vertexcount:0:int32:1") + .Data("MeshIndexCounts", "g3d:mesh:indexcount:0:int32:1") + .Data("MeshOpaqueVertexCounts", "g3d:mesh:opaquevertexcount:0:int32:1") + .Data("MeshOpaqueIndexCounts", "g3d:mesh:opaqueindexcount:0:int32:1"); + + public static G3dEntity materials = new G3dEntity("G3dMaterials") + .Data("MaterialColors", "g3d:material:color:0:float32:4") + .Data("MaterialGlossiness", "g3d:material:glossiness:0:float32:1") + .Data("MaterialSmoothness", "g3d:material:smoothness:0:float32:1"); + + + public static G3dEntity mesh = new G3dEntity("G3dChunk") + .Data("MeshOpaqueSubmeshCounts", "g3d:mesh:opaquesubmeshcount:0:int32:1") + .Index("MeshSubmeshOffset", "g3d:mesh:submeshoffset:0:int32:1", "Indices") + .Index("SubmeshIndexOffsets", "g3d:submesh:indexoffset:0:int32:1", "Indices") + .Index("SubmeshVertexOffsets", "g3d:submesh:vertexoffset:0:int32:1", "Indices") + .Index("SubmeshMaterials", "g3d:submesh:material:0:int32:1") + .Data("Positions", "g3d:vertex:position:0:float32:3") + .Index("Indices", "g3d:corner:index:0:int32:1", "Positions"); + } +} diff --git a/src/cs/g3d/Vim.G3d.CodeGen/G3dBuffer.cs b/src/cs/g3d/Vim.G3d.CodeGen/G3dBuffer.cs new file mode 100644 index 00000000..e3763042 --- /dev/null +++ b/src/cs/g3d/Vim.G3d.CodeGen/G3dBuffer.cs @@ -0,0 +1,48 @@ +using System; +using System.Diagnostics; + +namespace Vim.G3d.CodeGen +{ + public enum BufferType + { + Singleton, + Data, + Index + } + + /// + /// Holds all necessary data to generate the code for a g3dBuffer. + /// + public class G3dBuffer + { + public readonly string MemberName; + public readonly string BufferName; + public readonly BufferType BufferType; + public readonly Type ValueType; + public readonly string IndexInto; + + public string ArgumentName => LowerFirst(MemberName); + + public G3dBuffer(string name, string bufferName, BufferType bufferType, Type valueType, string indexInto = null) + { + Debug.Assert(bufferName.ToLower() == bufferName, "G3dCodeGen: Expected buffer name to be lowercase."); + + MemberName = name; + BufferName = bufferName; + BufferType = bufferType; + ValueType = valueType; + IndexInto = indexInto; + } + + public static string LowerFirst(string input) + { + if (string.IsNullOrEmpty(input)) + { + return input; + } + + return char.ToLower(input[0]) + input.Substring(1); + } + } +} + diff --git a/src/cs/g3d/Vim.G3d.CodeGen/G3dCodeGen.cs b/src/cs/g3d/Vim.G3d.CodeGen/G3dCodeGen.cs new file mode 100644 index 00000000..773342cc --- /dev/null +++ b/src/cs/g3d/Vim.G3d.CodeGen/G3dCodeGen.cs @@ -0,0 +1,147 @@ +using System; +using System.IO; +using System.Linq; + +namespace Vim.G3d.CodeGen +{ + public static class G3dCodeGen + { + public static void WriteDocument(string filePath) + { + try + { + var cb = new CodeBuilder(); + + cb.AppendLine("// AUTO-GENERATED FILE, DO NOT MODIFY."); + cb.AppendLine("// ReSharper disable All"); + cb.AppendLine("using Vim.BFastLib;"); + cb.AppendLine(); + cb.AppendLine("namespace Vim.G3d"); + cb.AppendLine("{"); + WriteEntities(cb); + cb.AppendLine("}"); + var content = cb.ToString(); + File.WriteAllText(filePath, content); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + + public static void WriteEntities(CodeBuilder cb) + { + foreach(var entity in Definitions.GetEntities()) + { + cb.AppendLine(EntityToCode(entity)); + } + } + + public static string EntityToCode(G3dEntity entity) + { + return $@"// Please provide an explicit implementation in another partial class file. + public partial class {entity.ClassName} : ISetup + {{ + {string.Join("\n \t\t", entity.Buffers.Select(b => + { + return $"public {b.ValueType}[] {b.MemberName};"; + })).TrimStart()} + + public {entity.ClassName}( + {string.Join(", \n \t\t\t", entity.Buffers.Select(b => + { + return $"{b.ValueType}[] {b.ArgumentName}"; + })).TrimStart()} + ) + {{ + {string.Join("\n \t\t\t", entity.Buffers.Select(b => + { + return $"{b.MemberName} = {b.ArgumentName};"; + })).TrimStart()} + + (this as ISetup).Setup(); + }} + + public {entity.ClassName}(BFast bfast) + {{ + {string.Join("\n \t\t\t", entity.Buffers.Select(b => + { + return $"{b.MemberName} = bfast.GetArray<{b.ValueType}>(\"{b.BufferName}\");"; + })).TrimStart()} + + (this as ISetup).Setup(); + }} + + public BFast ToBFast() + {{ + var bfast = new BFast(); + + {string.Join("\n \t\t\t", entity.Buffers.Select(b => + { + return $"bfast.SetArray<{b.ValueType}>(\"{b.BufferName}\", {b.MemberName});"; + })).TrimStart()} + + return bfast; + }} + + public {entity.ClassName} Clone() + {{ + return this.MemberwiseClone() as {entity.ClassName}; + }} + + public bool Equals({entity.ClassName} other ) + {{ + return {string.Join(" && \n \t\t\t", entity.Buffers.Select(b => + { + return $"BufferMethods.SafeEquals({b.MemberName}, other.{b.MemberName})"; + }))}; + }} + + public {entity.ClassName} Merge({entity.ClassName} other) + {{ + return new {entity.ClassName}( + {string.Join(", \n \t\t\t\t", entity.Buffers.Select(b => { + + switch (b.BufferType) + { + case BufferType.Singleton: + return $"{b.MemberName}"; + case BufferType.Data: + return $"BufferMethods.MergeData({b.MemberName}, other.{b.MemberName})"; + case BufferType.Index: + return $"BufferMethods.MergeIndex({b.MemberName}, other.{b.MemberName}, {b.IndexInto}?.Length ?? 0)"; + default: + return ""; + } + }))} + ); + }} + + public int CountOf(string name) + {{ + {string.Join("\n \t\t\t", entity.Buffers.Select(c => { + return $"if(name == \"{c.BufferName}\") return {c.MemberName}?.Length ?? -1;"; + }))} + return -1; + }} + + public void Validate() + {{ + // Ensure all the indices are either -1 or within the bounds of the attributes they are indexing into. + {string.Join("\n \t\t\t", entity.Buffers.Select(c => + { + if (c.BufferType == BufferType.Index) + { + return $"BufferMethods.ValidateIndex({c.MemberName}, {c.IndexInto}, \"{c.MemberName}\");"; + } + return null; + }).Where(s => s != null))} + }} + }} +"; + } + } +} + + diff --git a/src/cs/g3d/Vim.G3d.CodeGen/G3dEntity.cs b/src/cs/g3d/Vim.G3d.CodeGen/G3dEntity.cs new file mode 100644 index 00000000..5c224c31 --- /dev/null +++ b/src/cs/g3d/Vim.G3d.CodeGen/G3dEntity.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; + +namespace Vim.G3d.CodeGen +{ + /// + /// Holds the data to generate the code for a g3d entity. + /// + public class G3dEntity + { + public readonly string ClassName; + public readonly List Buffers = new List(); + + public G3dEntity(string name) + { + ClassName = name; + } + + public G3dEntity Index(string name, string bufferName, string indexInto = null) + { + if (indexInto == null) + { + return Data(name, bufferName); + } + Buffers.Add(new G3dBuffer(name, bufferName, BufferType.Index, typeof(int), indexInto)); + return this; + } + + public G3dEntity Data(string name, string bufferName, string indexInto = null) + { + Buffers.Add(new G3dBuffer(name, bufferName, BufferType.Data, typeof(T), indexInto)); + return this; + } + } +} diff --git a/src/cs/g3d/Vim.G3d.CodeGen/Program.cs b/src/cs/g3d/Vim.G3d.CodeGen/Program.cs new file mode 100644 index 00000000..6bf7d644 --- /dev/null +++ b/src/cs/g3d/Vim.G3d.CodeGen/Program.cs @@ -0,0 +1,11 @@ +namespace Vim.G3d.CodeGen +{ + public static class Program + { + public static void Main(string[] args) + { + var file = args[0]; + G3dCodeGen.WriteDocument(file); + } + } +} diff --git a/src/cs/g3d/Vim.G3d.CodeGen/Vim.G3d.CodeGen.csproj b/src/cs/g3d/Vim.G3d.CodeGen/Vim.G3d.CodeGen.csproj new file mode 100644 index 00000000..74a5126c --- /dev/null +++ b/src/cs/g3d/Vim.G3d.CodeGen/Vim.G3d.CodeGen.csproj @@ -0,0 +1,26 @@ + + + netstandard2.0;net6.0 + OnOutputUpdated + + + + Exe + + Vim.G3d.CodeGen.Program + + + + + + + + + + + + True + + + + \ No newline at end of file diff --git a/src/cs/g3d/Vim.G3d.Tests/G3dTestUtils.cs b/src/cs/g3d/Vim.G3d.Tests/G3dTestUtils.cs index 0a28e014..9bd3b033 100644 --- a/src/cs/g3d/Vim.G3d.Tests/G3dTestUtils.cs +++ b/src/cs/g3d/Vim.G3d.Tests/G3dTestUtils.cs @@ -1,137 +1,33 @@ -using Assimp; -using NUnit.Framework; -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using Vim.G3d.AssimpWrapper; -using Vim.LinqArray; +using Vim.Math3d; namespace Vim.G3d.Tests { public static class G3dTestUtils { - public static void OutputSceneStats(Scene scene) - => Console.WriteLine( -$@" #animations = {scene.AnimationCount} - #cameras = {scene.CameraCount} - #lights = {scene.LightCount} - #materials = {scene.MaterialCount} - #meshes = {scene.MeshCount} - #nodes = {scene.GetNodes().Count()} - #textures = {scene.TextureCount}"); - - // TODO: merge all of the meshes using the transform. - - public static void OutputMeshStats(Mesh mesh) - => Console.WriteLine( - $@" - mesh {mesh.Name} - #faces = {mesh.FaceCount} - #vertices = {mesh.VertexCount} - #normals = {mesh.Normals?.Count ?? 0} - #texture coordinate chanels = {mesh.TextureCoordinateChannelCount} - #vertex color chanels = {mesh.VertexColorChannelCount} - #bones = {mesh.BoneCount} - #tangents = {mesh.Tangents?.Count} - #bitangents = {mesh.BiTangents?.Count}"); - - public static T TimeLoadingFile(string fileName, Func func) - { - var sw = new Stopwatch(); - sw.Start(); - try - { - return func(fileName); - } - finally - { - Console.WriteLine($"Time to open {Path.GetFileName(fileName)} is {sw.ElapsedMilliseconds}msec"); - } - } - - public static void OutputStats(G3D g) + public static G3dVim CreateTestG3d() { - //Console.WriteLine("Header"); - - Console.WriteLine($"# corners per faces {g.NumCornersPerFace} "); - Console.WriteLine($"# vertices = {g.NumVertices}"); - Console.WriteLine($"# faces = {g.NumFaces}"); - Console.WriteLine($"# subgeos = {g.NumMeshes}"); - Console.WriteLine($"# indices (corners/edges0 = {g.NumCorners}"); - Console.WriteLine($"# instances = {g.NumInstances}"); - Console.WriteLine($"Number of attributes = {g.Attributes.Count}"); - - foreach (var attr in g.Attributes.ToEnumerable()) - Console.WriteLine($"{attr.Name} #items={attr.ElementCount}"); + var g3d = new G3dVim( + instanceTransforms: new Matrix4x4[] { Matrix4x4.Identity }, + instanceMeshes: new int[] { 0 }, + instanceParents: new int[] { -1 }, + instanceFlags: null, + meshSubmeshOffsets: new int[] { 0 }, + submeshIndexOffsets: new int[] { 0, 3, 6 }, + submeshMaterials: new int[] { 0 }, + indices: new int[] { 0, 1, 2, 0, 3, 2, 1, 3, 2 }, + positions: new Vector3[] { Vector3.Zero, Vector3.UnitX, Vector3.UnitY, Vector3.UnitZ }, + materialColors: new Vector4[] { new Vector4(0.25f, 0.5f, 0.75f, 1) }, + materialGlossiness: new float[] { 0.95f }, + materialSmoothness: new float[] { 0.5f }, + shapeColors: null, + shapeVertexOffsets: null, + shapeVertices: null, + shapeWidths: null + ); + g3d.Validate(); + + + return g3d; } - - public static void AssertSame(G3D g1, G3D g2) - { - Assert.AreEqual(g1.NumCornersPerFace, g2.NumCornersPerFace); - Assert.AreEqual(g1.NumFaces, g2.NumFaces); - Assert.AreEqual(g1.NumCorners, g2.NumCorners); - Assert.AreEqual(g1.NumVertices, g2.NumVertices); - Assert.AreEqual(g1.NumInstances, g2.NumInstances); - Assert.AreEqual(g1.NumMeshes, g2.NumMeshes); - Assert.AreEqual(g1.Attributes.Count, g2.Attributes.Count); - for (var i = 0; i < g1.Attributes.Count; ++i) - { - var attr1 = g1.Attributes[i]; - var attr2 = g2.Attributes[i]; - Assert.AreEqual(attr1.Name, attr2.Name); - Assert.AreEqual(attr1.GetByteSize(), attr2.GetByteSize()); - Assert.AreEqual(attr1.ElementCount, attr2.ElementCount); - } - } - - public static void AssertSame(Mesh m, G3D g) - { - Assert.AreEqual(m.FaceCount, g.NumFaces); - Assert.AreEqual(m.GetIndices(), g.Indices.ToArray()); - Assert.AreEqual(m.VertexCount, g.NumVertices); - } - - public static G3D CompareTiming(string fileName, string outputFolder) - { - using (var context = new AssimpContext()) - { - var scene = TimeLoadingFile(fileName, context.ImportFile); - var m = scene.Meshes[0]; - var g3d = m.ToG3D(); - AssertSame(m, g3d); - var outputFile = Path.Combine(outputFolder, Path.GetFileName(fileName) + ".g3d"); - g3d.Write(outputFile); - var r = TimeLoadingFile(outputFile, G3D.Read); - //OutputG3DStats(g3d); - AssertSame(g3d, r); - return r; - } - } - - public static string[] TestFiles = - { - @"models-nonbsd\3DS\jeep1.3ds", - @"models-nonbsd\3DS\mar_rifle.3ds", - @"models-nonbsd\dxf\rifle.dxf", - @"models-nonbsd\FBX\2013_ASCII\duck.fbx", - @"models-nonbsd\FBX\2013_ASCII\jeep1.fbx", - // Binary fails assimp import - //@"models-nonbsd\FBX\2013_BINARY\duck.fbx", - //@"models-nonbsd\FBX\2013_BINARY\jeep1.fbx", - // OBJ files were not checked in to the repo. - //@"models-nonbsd\OBJ\rifle.obj", - //@"models-nonbsd\OBJ\segment.obj", - @"models-nonbsd\PLY\ant-half.ply", - @"models\IFC\AC14-FZK-Haus.ifc", - @"models\PLY\Wuson.ply", - @"models\STL\Wuson.stl", - @"models\STL\Spider_ascii.stl", - @"models\STL\Spider_binary.stl", - @"models\glTF\CesiumMilkTruck\CesiumMilkTruck.gltf", - @"models\glTF2\2CylinderEngine-glTF-Binary\2CylinderEngine.glb", - @"models\DXF\wuson.dxf", - @"models\Collada\duck.dae", - }; } } diff --git a/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs b/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs index 363a020d..5e2dd0e9 100644 --- a/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs +++ b/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs @@ -1,377 +1,83 @@ -using Assimp; using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Vim.G3d.AssimpWrapper; -using Vim.LinqArray; -using Vim.Math3d; +using NUnit.Framework.Internal; +using Vim.BFastLib; +using Vim.Util.Tests; namespace Vim.G3d.Tests { - [TestFixture, Ignore("Ignored until the new version is ready")] - public static class G3dTests + [TestFixture] + public static class VimG3dTests { - public class FileLoadData - { - public FileLoadData(string filePath) - => SourceFile = new FileInfo(filePath); - - public string ShortName => Path.GetFileName(SourceFile.FullName); - public int NumMeshes => Scene?.MeshCount ?? 0; - public FileInfo SourceFile; - public FileInfo G3DFile; - public long MSecToOpen; - public long MSecToSaveG3d; - public long MSecToOpenG3d; - public long MSecToConvert; - public long MemoryConsumption; - public long MemoryConsumptionG3d; - public Exception Error; - public Scene Scene; - public G3D G3d; - } - - public static readonly string ProjectFolder = new DirectoryInfo(Properties.Resources.ProjDir.Trim()).FullName; - public static string RootFolder = Path.Combine(ProjectFolder, "..", "..", "..", ".."); - public static string TestInputFolder = Path.Combine(RootFolder, "data", "g3d-test-data", "models"); - public static string TestOutputFolder = Path.Combine(RootFolder, "data", "g3d-test-data", "output"); - - public static IEnumerable GetInputFiles() - => Directory.GetFiles(TestInputFolder, "*.*", SearchOption.AllDirectories); - - public static void ValidateSame(Object a, Object b, string name = "") - { - if (!a.Equals(b)) - throw new Exception($"Values {a} and {b} are different {name}"); - } - - public static void ValidateSameG3D(G3D g1, G3D g2) - { - ValidateSame(g1.NumCornersPerFace, g2.NumCornersPerFace, "NumCornersPerFace"); - ValidateSame(g1.NumFaces, g2.NumFaces, "NumFaces"); - ValidateSame(g1.NumCorners, g2.NumCorners, "NumCorners"); - ValidateSame(g1.NumVertices, g2.NumVertices, "NumVertices"); - ValidateSame(g1.NumInstances, g2.NumInstances, "NumInstances"); - ValidateSame(g1.NumMeshes, g2.NumMeshes, "NumMeshes"); - ValidateSame(g1.Attributes.Count, g2.Attributes.Count, "NumAttributes"); - for (var i = 0; i < g1.Attributes.Count; ++i) - { - var attr1 = g1.Attributes[i]; - var attr2 = g2.Attributes[i]; - ValidateSame(attr1.Name, attr2.Name, $"Attribute[{i}].Name"); - ValidateSame(attr1.GetByteSize(), attr2.GetByteSize(), $"Attribute[{i}].ByteSize"); - ValidateSame(attr1.ElementCount, attr2.ElementCount, $"Attribute[{i}].ElementCount"); - } - } - - [Test, Explicit("Use during debugging")] - public static void ReadG3DFiles() - { - foreach (var f in Directory.GetFiles(TestOutputFolder)) - { - var g3d = G3D.Read(f); - G3dTestUtils.OutputStats(g3d); - } - } + private static readonly string ResidencePath = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); [Test] - [Platform(Exclude = "Linux,Unix", Reason = "AssimpNet is failing to load its dependency on 'libdl.so'.")] - public static void OpenAndConvertAssimpFiles() + public static void Can_Read_G3d_From_Vim() { - var files = GetInputFiles() - .Where(AssimpLoader.CanLoad) - .Select(f => new FileLoadData(f)) - .ToArray(); - - // Load all the files - foreach (var f in files) - { - try - { - (f.MemoryConsumption, f.MSecToOpen) = - Util.GetMemoryConsumptionAndMSecElapsed(() => - f.Scene = AssimpLoader.Load(f.SourceFile.FullName)); - } - catch (Exception e) - { - f.Error = e; - } - } - - // Convert all the Assimp scenes to G3D - foreach (var f in files) - { - if (f.Scene == null) continue; - - try - { - f.MSecToConvert = Util.GetMSecElapsed(() => - f.G3d = f.Scene.ToG3d()); - } - catch (Exception e) - { - f.Error = e; - } - } - - // Save all the G3D scenes - Util.CreateAndClearDirectory(TestOutputFolder); - foreach (var f in files) - { - if (f.G3d == null) continue; - - try - { - var outputFilePath = Path.Combine(TestOutputFolder, f.ShortName + ".g3d"); - f.G3DFile = new FileInfo(outputFilePath); - - f.MSecToSaveG3d = Util.GetMSecElapsed(() => - f.G3d.Write(outputFilePath)); - } - catch (Exception e) - { - f.Error = e; - } - } - - // Try reading back in all of the G3D scenes, measure load times and the memory consumption - foreach (var f in files) - { - if (f.G3DFile == null) continue; - - try - { - G3D localG3d = null; - - (f.MemoryConsumptionG3d, f.MSecToOpenG3d) = - Util.GetMemoryConsumptionAndMSecElapsed(() => - localG3d = G3D.Read(f.G3DFile.FullName)); - - ValidateSameG3D(f.G3d, localG3d); - } - catch (Exception e) - { - f.Error = e; - } - } - - // Output the header for data - Console.WriteLine( - "Importer," + - "Extension," + - "File Name," + - "File Size(KB)," + - "Load Time(s)," + - "Memory(KB)," + - "# Meshes," + - "Time to Convert," + - "Time to Write G3D," + - "G3D File Size(KB)," + - "G3D Memory(KB)", - "G3D Load Time(s)", - "Error"); - - // Output the data rows - foreach (var f in files) - { - Console.WriteLine( - "Assimp," + - $"{Path.GetExtension(f.ShortName)}," + - $"{f.ShortName}," + - $"{f.SourceFile?.Length / 1000}," + - $"{f.MSecToOpen / 100f}," + - $"{f.MemoryConsumption / 1000}," + - $"{f.NumMeshes}," + - $"{f.MSecToConvert / 100f}," + - $"{f.MSecToSaveG3d / 100f}," + - $"{f.G3DFile?.Length / 1000}," + - $"{f.MemoryConsumptionG3d / 1000}," + - $"{f.MSecToOpenG3d / 100f}," + - $"{f.Error}"); - } - - Assert.AreEqual(0, files.Count(f => f.Error != null), "Errors occurred"); + var g3d = G3dVim.FromVim(ResidencePath); + Assert.IsNotNull(g3d); } [Test] - public static void TriangleTest() + public static void Can_Ignore_Extra_Attributes() { - // Serialize a triangle g3d as bytes and read it back. - var vertices = new[] - { - new Vector3(0, 0, 0), - new Vector3(0, 1, 0), - new Vector3(0, 1, 1) - }; - - var indices = new[] { 0, 1, 2 }; - var materialIndices = new[] { 5 }; - - var g3d = new G3DBuilder() - .AddVertices(vertices.ToIArray()) - .AddIndices(indices.ToIArray()) - .Add(materialIndices.ToIArray().ToFaceMaterialAttribute()) - .ToG3D(); - - var bytes = g3d.WriteToBytes(); - var g = G3D.Read(bytes); - - Assert.IsNotNull(g); - - Assert.AreEqual(3, g.NumVertices); - Assert.AreEqual(3, g.NumCorners); - Assert.AreEqual(1, g.NumFaces); - Assert.AreEqual(3, g.NumCornersPerFace); - Assert.AreEqual(0, g.NumMeshes); - Assert.AreEqual(0, g.NumInstances); - - Assert.AreEqual(vertices, g.Vertices.ToArray()); - Assert.AreEqual(indices, g.Indices.ToArray()); - Assert.AreEqual(materialIndices, g.FaceMaterials.ToArray()); + // Both G3dVim and G3dMaterial share 3 attributes + // G3dVim contains many more attributes + // We create a g3dMaterial from the bytes of a g3dVim + // Shows that extra attributes are ignored as they should. + + var g3d = G3dTestUtils.CreateTestG3d(); + var g3dMats = new G3dMaterials(g3d.ToBFast()); + + Assert.IsNotNull(g3dMats); + Assert.AreEqual(g3d.MaterialColors, g3dMats.MaterialColors); + Assert.AreEqual(g3d.MaterialGlossiness, g3dMats.MaterialGlossiness); + Assert.AreEqual(g3d.MaterialSmoothness, g3dMats.MaterialSmoothness); } [Test] - public static void QuadAndCopyTest() + public static void Can_Write_And_Read() { - // Serialize a triangle g3d as bytes and read it back. - var vertices = new[] - { - new Vector3(0, 0, 0), - new Vector3(0, 1, 0), - new Vector3(0, 1, 1), - new Vector3(1, 1, 1) - }; - - var indices = new[] { 0, 1, 2, 3 }; - var materialIndices = new[] { 5 }; - - var g3d = new G3DBuilder() - .AddVertices(vertices.ToIArray()) - .AddIndices(indices.ToIArray()) - .Add(materialIndices.ToIArray().ToFaceMaterialAttribute()) - .ToG3D(); - - var bytes = g3d.WriteToBytes(); - var g = G3D.Read(bytes); - - Assert.IsNotNull(g); - - Assert.AreEqual(4, g.NumCornersPerFace); - Assert.AreEqual(4, g.NumVertices); - Assert.AreEqual(4, g.NumCorners); - Assert.AreEqual(1, g.NumFaces); - Assert.AreEqual(0, g.NumMeshes); - Assert.AreEqual(0, g.NumInstances); - - Assert.AreEqual(vertices, g.Vertices.ToArray()); - Assert.AreEqual(indices, g.Indices.ToArray()); - Assert.AreEqual(materialIndices, g.FaceMaterials.ToArray()); - - var g2 = g.TriangulateQuadMesh(); - - Assert.AreEqual(3, g2.NumCornersPerFace); - Assert.AreEqual(4, g2.NumVertices); - Assert.AreEqual(6, g2.NumCorners); - Assert.AreEqual(2, g2.NumFaces); - Assert.AreEqual(0, g2.NumMeshes); - Assert.AreEqual(0, g2.NumInstances); - - Assert.AreEqual(vertices, g2.GetAttributeDataPosition().ToArray()); - Assert.AreEqual(new[] { 0, 1, 2, 0, 2, 3 }, g2.GetAttributeDataIndex().ToArray()); - Assert.AreEqual(new[] { 5, 5 }, g2.GetAttributeDataFaceMaterial().ToArray()); - - g2 = g2.CopyFaces(1, 1); - - Assert.AreEqual(3, g2.NumCornersPerFace); - Assert.AreEqual(4, g2.NumVertices); - Assert.AreEqual(3, g2.NumCorners); - Assert.AreEqual(1, g2.NumFaces); - - Assert.AreEqual(vertices, g2.GetAttributeDataPosition().ToArray()); - Assert.AreEqual(new[] { 0, 2, 3 }, g2.GetAttributeDataIndex().ToArray()); - Assert.AreEqual(new[] { 5 }, g2.GetAttributeDataFaceMaterial().ToArray()); + var expected = G3dTestUtils.CreateTestG3d(); + var g3d = new G3dVim(expected.ToBFast()); + Assert.IsTrue(g3d.Equals(expected)); } - [Test] - public static void UnexpectedAttributeTest() - { - // This test validates that unrecognized g3d attributes are simply ignored in the deserialization process. - // - // "unexpected.g3d" was generated using the following code snippet. Note that the code was temporarily modified such - // that UNKNOWN mapped to a ulong data type value (8 bits): - // - // var g3d = new G3DBuilder() - // .Add(new GeometryAttribute(new int[] { 5 }.ToIArray(), AttributeDescriptor.Parse("g3d:instance:potato:0:int32:1"))) - // .Add(new GeometryAttribute(new ulong[] { 42 }.ToIArray(), AttributeDescriptor.Parse("g3d:instance:beep:0:UNKNOWN:1"))) - // .ToG3D(); - var g = G3D.Read(Path.Combine(TestInputFolder, "unexpected.g3d")); - - var parsedInstanceAttrs = g.Attributes.Where(ga => ga.Descriptor.Association == Association.assoc_instance).ToArray(); - Assert.AreEqual(1, parsedInstanceAttrs.Length); // NOTE: we only expect one attribute (the one with the "potato" semantic) because UNKNOWN is currently ignored as a datatype. - var parsedPotatoAttr = parsedInstanceAttrs.Single(ga => ga.Descriptor.Semantic == "potato"); - Assert.AreEqual(new [] { 5 }, parsedPotatoAttr.AsType().Data.ToArray()); - } - public static G3D LoadAssimpFile(string filePath) + [Test] + public static void Can_Merge_two_g3d() { - using (var context = new AssimpContext()) - { - var scene = context.ImportFile(filePath); - Assert.AreEqual(1, scene.Meshes.Count); - return scene.Meshes[0].ToG3D(); - } + var g3d = G3dTestUtils.CreateTestG3d(); + var merged = g3d.Merge(g3d); + + var expected = new G3dVim( + instanceTransforms: g3d.InstanceTransforms.Concat(g3d.InstanceTransforms).ToArray(), + instanceMeshes: g3d.InstanceMeshes.Concat(g3d.InstanceMeshes.Select(i => i + g3d.GetMeshCount())).ToArray(), + instanceParents: g3d.InstanceParents.Concat(g3d.InstanceParents).ToArray(), + instanceFlags: null, + meshSubmeshOffsets: g3d.MeshSubmeshOffsets.Concat(g3d.MeshSubmeshOffsets.Select(i => g3d.GetSubmeshCount())).ToArray(), + submeshIndexOffsets: g3d.SubmeshIndexOffsets.Concat(g3d.SubmeshIndexOffsets.Select(i => i + g3d.GetIndexCount())).ToArray(), + submeshMaterials: g3d.SubmeshMaterials.Concat(g3d.SubmeshMaterials.Select(i => i + g3d.GetMaterialCount())).ToArray(), + indices: g3d.Indices.Concat(g3d.Indices.Select(i => i + g3d.Positions.Length)).ToArray(), + positions: g3d.Positions.Concat(g3d.Positions).ToArray(), + materialColors: g3d.MaterialColors.Concat(g3d.MaterialColors).ToArray(), + materialGlossiness: g3d.MaterialGlossiness.Concat(g3d.MaterialGlossiness).ToArray(), + materialSmoothness: g3d.MaterialSmoothness.Concat(g3d.MaterialSmoothness).ToArray(), + shapeColors: null, + shapeWidths: null, + shapeVertices: null, + shapeVertexOffsets: null + ); + Assert.IsTrue(merged.Equals(expected)); } - // NOTE: can't be run as part of NUnit because it requires the GC - public static void BigFileTest() - { - var nVerts = (300 * 1000 * 1000); // 300 * 12 = 3.6 GB - var vertices = nVerts.Select(i => new Vector3(i, i, i)); - var bldr = new G3DBuilder(); - bldr.AddVertices(vertices); - var g3d = bldr.ToG3D(); - Assert.AreEqual(nVerts, g3d.NumVertices); - var tempFile = Path.Combine(Path.GetTempPath(), "bigfile.g3d"); - g3d.Write(tempFile); - var tmp = G3D.Read(tempFile); - ValidateSameG3D(g3d, tmp); - } [Test] - [Platform(Exclude = "Linux,Unix", Reason = "AssimpNet is failing to load its dependency on 'libdl.so'.")] - public static void TestWriters() + public static void Clone_IsEqual() { - var fileName = Path.Combine(TestInputFolder, "PLY", "Wuson.ply"); - - var outputFileName = @"test"; - outputFileName = Path.Combine(TestOutputFolder, outputFileName); - - var g3d = LoadAssimpFile(fileName); - - g3d.WritePly(outputFileName + ".ply"); - g3d.WriteObj(outputFileName + ".obj"); - - // TODO compare the PLY, the OBJ and the original file. - - var g3dFromPly = LoadAssimpFile(outputFileName + ".ply"); - //var g3dFromObj = LoadAssimpFile(outputFileName + ".obj"); - - { - var g1 = g3d; - var g2 = g3dFromPly; - Assert.AreEqual(g1.NumCornersPerFace, g2.NumCornersPerFace); - Assert.AreEqual(g1.NumFaces, g2.NumFaces); - Assert.AreEqual(g1.NumCorners, g2.NumCorners); - Assert.AreEqual(g1.NumVertices, g2.NumVertices); - Assert.AreEqual(g1.NumInstances, g2.NumInstances); - Assert.AreEqual(g1.NumMeshes, g2.NumMeshes); - } - - // BUG: Assimp ignores the OBJ index buffer. God knows why. - //CompareG3D(g3d, g3dFromObj); + var g3d = G3dTestUtils.CreateTestG3d(); + Assert.IsTrue(g3d.Equals(g3d.Clone())); } } } + diff --git a/src/cs/g3d/Vim.G3d.Tests/Properties/Resources.Designer.cs b/src/cs/g3d/Vim.G3d.Tests/Properties/Resources.Designer.cs deleted file mode 100644 index 2f5f4dfd..00000000 --- a/src/cs/g3d/Vim.G3d.Tests/Properties/Resources.Designer.cs +++ /dev/null @@ -1,73 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Vim.G3d.Tests.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Vim.G3d.Tests.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to C:\DEV\g3d\csharp\Vim.G3d.Tests\ - ///. - /// - internal static string ProjDir { - get { - return ResourceManager.GetString("ProjDir", resourceCulture); - } - } - } -} diff --git a/src/cs/g3d/Vim.G3d.Tests/Properties/Resources.resx b/src/cs/g3d/Vim.G3d.Tests/Properties/Resources.resx deleted file mode 100644 index 579dd3b6..00000000 --- a/src/cs/g3d/Vim.G3d.Tests/Properties/Resources.resx +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - ..\Resources\ProjDir.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 - - \ No newline at end of file diff --git a/src/cs/g3d/Vim.G3d.Tests/Resources/.gitignore b/src/cs/g3d/Vim.G3d.Tests/Resources/.gitignore deleted file mode 100644 index d8fff56a..00000000 --- a/src/cs/g3d/Vim.G3d.Tests/Resources/.gitignore +++ /dev/null @@ -1 +0,0 @@ -ProjDir.txt \ No newline at end of file diff --git a/src/cs/g3d/Vim.G3d.Tests/Util.cs b/src/cs/g3d/Vim.G3d.Tests/Util.cs deleted file mode 100644 index 2ff62684..00000000 --- a/src/cs/g3d/Vim.G3d.Tests/Util.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; - -namespace Vim.G3d.Tests -{ - public static class Util - { - public static (long, long) GetMemoryConsumptionAndMSecElapsed(Action action) - { - var time = 0L; - var mem = GetMemoryConsumption( - () => time = GetMSecElapsed(action)); - return (mem, time); - } - - public static long GetMSecElapsed(Action action) - { - var sw = Stopwatch.StartNew(); - action(); - return sw.ElapsedMilliseconds; - } - - /// - /// Creates a directory if needed, or clears all of its contents otherwise - /// - public static string CreateAndClearDirectory(string dirPath) - { - if (!Directory.Exists(dirPath)) - Directory.CreateDirectory(dirPath); - else - DeleteFolderContents(dirPath); - return dirPath; - } - - /// - /// Deletes all contents in a folder - /// - /// - /// https://stackoverflow.com/questions/1288718/how-to-delete-all-files-and-folders-in-a-directory - /// - private static void DeleteFolderContents(string folderPath) - { - var di = new DirectoryInfo(folderPath); - foreach (var dir in di.EnumerateDirectories().AsParallel()) - DeleteFolderAndAllContents(dir.FullName); - foreach (var file in di.EnumerateFiles().AsParallel()) - file.Delete(); - } - - /// - /// Deletes everything in a folder and then the folder. - /// - private static void DeleteFolderAndAllContents(string folderPath) - { - if (!Directory.Exists(folderPath)) - return; - - DeleteFolderContents(folderPath); - Directory.Delete(folderPath); - } - - // NOTE: Calling a function generates additional memory - private static long GetMemoryConsumption(Action action) - { - GC.Collect(); - GC.WaitForPendingFinalizers(); - var memBefore = GC.GetTotalMemory(true); - action(); - GC.Collect(); - GC.WaitForPendingFinalizers(); - return GC.GetTotalMemory(true) - memBefore; - } - } -} \ No newline at end of file diff --git a/src/cs/g3d/Vim.G3d.Tests/Vim.G3d.Tests.csproj b/src/cs/g3d/Vim.G3d.Tests/Vim.G3d.Tests.csproj index 285c4437..e56190a6 100644 --- a/src/cs/g3d/Vim.G3d.Tests/Vim.G3d.Tests.csproj +++ b/src/cs/g3d/Vim.G3d.Tests/Vim.G3d.Tests.csproj @@ -1,37 +1,29 @@  - - net6.0 - false - + + net6.0 + enable + enable - - - - - + false + true + - - - - + + + + + - - - True - True - Resources.resx - - + + + + + + + True + + + - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - - - diff --git a/src/cs/g3d/Vim.G3d/AttributeDescriptor.cs b/src/cs/g3d/Vim.G3d/AttributeDescriptor.cs deleted file mode 100644 index a2f48840..00000000 --- a/src/cs/g3d/Vim.G3d/AttributeDescriptor.cs +++ /dev/null @@ -1,158 +0,0 @@ -using System; - -namespace Vim.G3d -{ - /// - /// Provides information about identifying the role and parsing the data within an attribute data buffer. - /// This is encoded using a string in a particular URN form. - /// - public class AttributeDescriptor - { - public Association Association { get; } - public string Semantic { get; } - public DataType DataType { get; } - public int DataArity { get; } - public int Index { get; } - - public int DataElementSize { get; } - public int DataTypeSize { get; } - public string Name { get; } - - public AttributeDescriptor(Association association, string semantic, DataType dataType, int dataArity, int index = 0) - { - Association = association; - if (semantic.Contains(":")) - throw new Exception("The semantic must not contain a semicolon"); - Semantic = semantic; - DataType = dataType; - DataArity = dataArity; - Index = index; - DataTypeSize = GetDataTypeSize(DataType); - DataElementSize = DataTypeSize * DataArity; - Name = $"g3d:{AssociationString}:{Semantic}:{Index}:{DataTypeString}:{DataArity}"; - } - - /// - /// Generates a URN representation of the attribute descriptor - /// - public override string ToString() - => Name; - - /// - /// Returns true if the attribute descriptor has been successfully parsed. - /// - public static bool TryParse(string urn, out AttributeDescriptor attributeDescriptor) - { - attributeDescriptor = null; - try - { - attributeDescriptor = Parse(urn); - } - catch - { - // do nothing. - } - - return attributeDescriptor != null; - } - - /// - /// Parses a URN representation of the attribute descriptor to generate an actual attribute descriptor - /// - public static AttributeDescriptor Parse(string urn) - { - var vals = urn.Split(':'); - if (vals.Length != 6) throw new Exception("Expected 6 parts to the attribute descriptor URN"); - if (vals[0] != "g3d") throw new Exception("First part of URN must be g3d"); - return new AttributeDescriptor( - ParseAssociation(vals[1]), - vals[2], - ParseDataType(vals[4]), - int.Parse(vals[5]), - int.Parse(vals[3]) - ); - } - - public bool Validate() - { - var urn = ToString(); - var tmp = Parse(urn); - if (!Equals(tmp)) - throw new Exception("Invalid attribute descriptor (or internal error in the parsing/string conversion"); - return true; - } - - public bool Equals(AttributeDescriptor other) - => ToString() == other.ToString(); - - public static int GetDataTypeSize(DataType dt) - { - switch (dt) - { - case DataType.dt_uint8: - case DataType.dt_int8: - return 1; - case DataType.dt_uint16: - case DataType.dt_int16: - return 2; - case DataType.dt_uint32: - case DataType.dt_int32: - return 4; - case DataType.dt_uint64: - case DataType.dt_int64: - return 8; - case DataType.dt_float32: - return 4; - case DataType.dt_float64: - return 8; - default: - throw new ArgumentOutOfRangeException(nameof(dt), dt, null); - } - } - - public string AssociationString - => Association.ToString().Substring("assoc_".Length); - - public static Association ParseAssociation(string s) - { - switch (s) - { - case "all": - return Association.assoc_all; - case "corner": - return Association.assoc_corner; - case "edge": - return Association.assoc_edge; - case "face": - return Association.assoc_face; - case "instance": - return Association.assoc_instance; - case "vertex": - return Association.assoc_vertex; - case "shapevertex": - return Association.assoc_shapevertex; - case "shape": - return Association.assoc_shape; - case "material": - return Association.assoc_material; - case "mesh": - return Association.assoc_mesh; - case "submesh": - return Association.assoc_submesh; - - // Anything else we just treat as unknown - default: - return Association.assoc_none; - } - } - - public string DataTypeString - => DataType.ToString()?.Substring("dt_".Length) ?? null; - - public static DataType ParseDataType(string s) - => (DataType)Enum.Parse(typeof(DataType), "dt_" + s); - - public AttributeDescriptor SetIndex(int index) - => new AttributeDescriptor(Association, Semantic, DataType, DataArity, index); - } -} diff --git a/src/cs/g3d/Vim.G3d/AttributeExtensions.cs b/src/cs/g3d/Vim.G3d/AttributeExtensions.cs deleted file mode 100644 index 98fb02fa..00000000 --- a/src/cs/g3d/Vim.G3d/AttributeExtensions.cs +++ /dev/null @@ -1,150 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.G3d -{ - public static class AttributeExtensions - { - public static GeometryAttribute CheckArity(this GeometryAttribute self, int arity) where T : unmanaged - => self?.Descriptor?.DataArity == arity ? self : null; - - public static GeometryAttribute CheckAssociation(this GeometryAttribute self, Association assoc) where T : unmanaged - => self?.Descriptor?.Association == assoc ? self : null; - - public static GeometryAttribute CheckArityAndAssociation(this GeometryAttribute self, int arity, Association assoc) where T : unmanaged - => self?.CheckArity(arity)?.CheckAssociation(assoc); - - public static GeometryAttribute ToAttribute(this IList self, string desc) where T : unmanaged - => self.ToIArray().ToAttribute(desc); - - public static GeometryAttribute ToAttribute(this IList self, AttributeDescriptor desc) where T : unmanaged - => self.ToIArray().ToAttribute(desc); - - public static GeometryAttribute ToAttribute(this IArray self, AttributeDescriptor desc) where T : unmanaged - => new GeometryAttribute(self, desc); - - public static GeometryAttribute ToAttribute(this IArray self, string desc) where T : unmanaged - => self.ToAttribute(AttributeDescriptor.Parse(desc)); - - public static GeometryAttribute ToAttribute(this IArray self, string desc, int index) where T : unmanaged - => self.ToAttribute(AttributeDescriptor.Parse(desc).SetIndex(index)); - - public static IArray AttributeToColors(this GeometryAttribute attr) - { - var desc = attr.Descriptor; - if (desc.DataType == DataType.dt_float32) - { - if (desc.DataArity == 4) - return attr.AsType().Data; - if (desc.DataArity == 3) - return attr.AsType().Data.Select(vc => new Vector4(vc, 1f)); - if (desc.DataArity == 2) - return attr.AsType().Data.Select(vc => new Vector4(vc.X, vc.Y, 0, 1f)); - if (desc.DataArity == 1) - return attr.AsType().Data.Select(vc => new Vector4(vc, vc, vc, 1f)); - } - if (desc.DataType == DataType.dt_int8) - { - if (desc.DataArity == 4) - return attr.AsType().Data.Select(b => new Vector4(b.X / 255f, b.Y / 255f, b.Z / 255f, b.W / 255f)); - if (desc.DataArity == 3) - return attr.AsType().Data.Select(b => new Vector4(b.X / 255f, b.Y / 255f, b.Z / 255f, 1f)); - if (desc.DataArity == 2) - return attr.AsType().Data.Select(b => new Vector4(b.X / 255f, b.Y / 255f, 0f, 1f)); - if (desc.DataArity == 1) - return attr.AsType().Data.Select(b => new Vector4(b / 255f, b / 255f, b / 255f, 1f)); - } - Debug.WriteLine($"Failed to recongize color format {attr.Descriptor}"); - return null; - } - - public static GeometryAttribute ToDefaultAttribute(this AttributeDescriptor desc, int count) - { - switch (desc.DataType) - { - // TODO: TECH DEBT - Add unsigned tuple objects to Math3d - case DataType.dt_uint8: - if (desc.DataArity == 1) - return default(byte).Repeat(count).ToAttribute(desc); - break; - case DataType.dt_int8: - if (desc.DataArity == 1) - return default(byte).Repeat(count).ToAttribute(desc); - if (desc.DataArity == 2) - return default(Byte2).Repeat(count).ToAttribute(desc); - if (desc.DataArity == 3) - return default(Byte3).Repeat(count).ToAttribute(desc); - if (desc.DataArity == 4) - return default(Byte4).Repeat(count).ToAttribute(desc); - break; - case DataType.dt_uint16: - if (desc.DataArity == 1) - return default(ushort).Repeat(count).ToAttribute(desc); - break; - case DataType.dt_int16: - if (desc.DataArity == 1) - return default(short).Repeat(count).ToAttribute(desc); - break; - case DataType.dt_uint32: - if (desc.DataArity == 1) - return default(uint).Repeat(count).ToAttribute(desc); - break; - case DataType.dt_int32: - if (desc.DataArity == 1) - return default(int).Repeat(count).ToAttribute(desc); - if (desc.DataArity == 2) - return default(Int2).Repeat(count).ToAttribute(desc); - if (desc.DataArity == 3) - return default(Int3).Repeat(count).ToAttribute(desc); - if (desc.DataArity == 4) - return default(Int4).Repeat(count).ToAttribute(desc); - break; - case DataType.dt_uint64: - if (desc.DataArity == 1) - return default(ulong).Repeat(count).ToAttribute(desc); - break; - case DataType.dt_int64: - if (desc.DataArity == 1) - return default(long).Repeat(count).ToAttribute(desc); - break; - case DataType.dt_float32: - if (desc.DataArity == 1) - return default(float).Repeat(count).ToAttribute(desc); - if (desc.DataArity == 2) - return default(Vector2).Repeat(count).ToAttribute(desc); - if (desc.DataArity == 3) - return default(Vector3).Repeat(count).ToAttribute(desc); - if (desc.DataArity == 4) - return default(Vector4).Repeat(count).ToAttribute(desc); - if (desc.DataArity == 16) - return default(Matrix4x4).Repeat(count).ToAttribute(desc); - break; - case DataType.dt_float64: - if (desc.DataArity == 1) - return default(double).Repeat(count).ToAttribute(desc); - if (desc.DataArity == 2) - return default(DVector2).Repeat(count).ToAttribute(desc); - if (desc.DataArity == 3) - return default(DVector3).Repeat(count).ToAttribute(desc); - if (desc.DataArity == 4) - return default(DVector4).Repeat(count).ToAttribute(desc); - break; - } - - throw new Exception($"Could not create a default attribute for {desc}"); - } - - public static long GetByteSize(this GeometryAttribute attribute) - => (long)attribute.ElementCount * attribute.Descriptor.DataElementSize; - - public static GeometryAttribute Merge(this IEnumerable attributes) - => attributes.FirstOrDefault()?.Merge(attributes.Skip(1)); - - public static GeometryAttribute Merge(this IArray attributes) - => attributes.ToEnumerable().Merge(); - } -} diff --git a/src/cs/g3d/Vim.G3d/BufferMethods.cs b/src/cs/g3d/Vim.G3d/BufferMethods.cs new file mode 100644 index 00000000..c7330f7e --- /dev/null +++ b/src/cs/g3d/Vim.G3d/BufferMethods.cs @@ -0,0 +1,63 @@ +using System; +using System.IO; +using System.Linq; + +namespace Vim.G3d +{ + public static class BufferMethods + { + public static bool SafeEquals(T[] a, T[] b) + { + if(a == null && b == null) return true; + if(a == null) return false; + if(b == null) return false; + if(a.Length != b.Length) return false; + for(var i=0; i(T[] a, T[] b) + { + if(a == null && b == null) return null; + if(a == null) { return b.ToArray(); } + if(b == null) { return a.ToArray(); } + var result = new T[a.Length + b.Length]; + Array.Copy(a, result, a.Length); + Array.Copy(b, 0, result, a.Length, b.Length); + return result; + } + + public static int[] MergeIndex(int[] a, int[] b, int offset) + { + if (a == null && b == null) return null; + if (a == null && b == null) return null; + if (a == null) { return b.ToArray(); } + if (b == null) { return a.ToArray(); } + var result = new int[a.Length + b.Length]; + Array.Copy(a, result, a.Length); + for(var i=0; i= 0 + ? offset + b[i] + : -1; + } + return result; + } + + public static void ValidateIndex(int[] array, T[] into, string name) + { + if (array == null) return; + var max = into?.Length -1 ?? int.MaxValue; + for(var i=0; i < array.Length; i++) + { + if (array[i] < -1 || array[i] > max) + { + throw new InvalidDataException($"Invalid value {array[i]} in {name} buffer."); + } + } + } + } +} diff --git a/src/cs/g3d/Vim.G3d/CommonAttributes.cs b/src/cs/g3d/Vim.G3d/CommonAttributes.cs deleted file mode 100644 index da835913..00000000 --- a/src/cs/g3d/Vim.G3d/CommonAttributes.cs +++ /dev/null @@ -1,231 +0,0 @@ - -// AUTOGENERATED FILE: DO NOT EDIT -// This file is generated from CommonAttributeExtensions.tt - - -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.G3d -{ - public static class CommonAttributes - { - - public const string ObjectFaceSize = "g3d:all:facesize:0:int32:1"; - public const string Index = "g3d:corner:index:0:int32:1"; - public const string Position = "g3d:vertex:position:0:float32:3"; - public const string VertexUv = "g3d:vertex:uv:0:float32:2"; - public const string VertexUvw = "g3d:vertex:uv:0:float32:3"; - public const string VertexNormal = "g3d:vertex:normal:0:float32:3"; - public const string VertexColor = "g3d:vertex:color:0:float32:4"; - public const string VertexColor8Bit = "g3d:vertex:color:0:int8:4"; - public const string VertexBitangent = "g3d:vertex:bitangent:0:float32:3"; - public const string VertexTangent = "g3d:vertex:tangent:0:float32:4"; - public const string VertexSelectionWeight = "g3d:vertex:weight:0:float32:1"; - public const string FaceColor = "g3d:face:color:0:float32:4"; - public const string FaceMaterial = "g3d:face:material:0:int32:1"; - public const string FaceNormal = "g3d:face:normal:0:float32:3"; - public const string MeshSubmeshOffset = "g3d:mesh:submeshoffset:0:int32:1"; - public const string InstanceTransform = "g3d:instance:transform:0:float32:16"; - public const string InstanceParent = "g3d:instance:parent:0:int32:1"; - public const string InstanceMesh = "g3d:instance:mesh:0:int32:1"; - public const string InstanceFlags = "g3d:instance:flags:0:uint16:1"; - public const string LineTangentIn = "g3d:vertex:tangent:0:float32:3"; - public const string LineTangentOut = "g3d:vertex:tangent:1:float32:3"; - public const string ShapeVertex = "g3d:shapevertex:position:0:float32:3"; - public const string ShapeVertexOffset = "g3d:shape:vertexoffset:0:int32:1"; - public const string ShapeColor = "g3d:shape:color:0:float32:4"; - public const string ShapeWidth = "g3d:shape:width:0:float32:1"; - public const string MaterialColor = "g3d:material:color:0:float32:4"; - public const string MaterialGlossiness = "g3d:material:glossiness:0:float32:1"; - public const string MaterialSmoothness = "g3d:material:smoothness:0:float32:1"; - public const string SubmeshIndexOffset = "g3d:submesh:indexoffset:0:int32:1"; - public const string SubmeshMaterial = "g3d:submesh:material:0:int32:1"; - } - - public static class CommonAttributeExtensions - { - - public static GeometryAttribute ToObjectFaceSizeAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.ObjectFaceSize, index); - public static GeometryAttribute ToObjectFaceSizeAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.ObjectFaceSize); - public static GeometryAttribute ToObjectFaceSizeAttribute(this int[] xs, int index) => xs.ToIArray().ToObjectFaceSizeAttribute(index); - public static GeometryAttribute ToObjectFaceSizeAttribute(this int[] xs) => xs.ToIArray().ToObjectFaceSizeAttribute(); - public static GeometryAttribute GetAttributeObjectFaceSize(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.ObjectFaceSize); - public static IArray GetAttributeDataObjectFaceSize(this IGeometryAttributes self) => self.GetAttributeObjectFaceSize()?.Data; - public static GeometryAttribute ToIndexAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.Index, index); - public static GeometryAttribute ToIndexAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.Index); - public static GeometryAttribute ToIndexAttribute(this int[] xs, int index) => xs.ToIArray().ToIndexAttribute(index); - public static GeometryAttribute ToIndexAttribute(this int[] xs) => xs.ToIArray().ToIndexAttribute(); - public static GeometryAttribute GetAttributeIndex(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.Index); - public static IArray GetAttributeDataIndex(this IGeometryAttributes self) => self.GetAttributeIndex()?.Data; - public static GeometryAttribute ToPositionAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.Position, index); - public static GeometryAttribute ToPositionAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.Position); - public static GeometryAttribute ToPositionAttribute(this Vector3[] xs, int index) => xs.ToIArray().ToPositionAttribute(index); - public static GeometryAttribute ToPositionAttribute(this Vector3[] xs) => xs.ToIArray().ToPositionAttribute(); - public static GeometryAttribute GetAttributePosition(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.Position); - public static IArray GetAttributeDataPosition(this IGeometryAttributes self) => self.GetAttributePosition()?.Data; - public static GeometryAttribute ToVertexUvAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.VertexUv, index); - public static GeometryAttribute ToVertexUvAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.VertexUv); - public static GeometryAttribute ToVertexUvAttribute(this Vector2[] xs, int index) => xs.ToIArray().ToVertexUvAttribute(index); - public static GeometryAttribute ToVertexUvAttribute(this Vector2[] xs) => xs.ToIArray().ToVertexUvAttribute(); - public static GeometryAttribute GetAttributeVertexUv(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexUv); - public static IArray GetAttributeDataVertexUv(this IGeometryAttributes self) => self.GetAttributeVertexUv()?.Data; - public static GeometryAttribute ToVertexUvwAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.VertexUvw, index); - public static GeometryAttribute ToVertexUvwAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.VertexUvw); - public static GeometryAttribute ToVertexUvwAttribute(this Vector3[] xs, int index) => xs.ToIArray().ToVertexUvwAttribute(index); - public static GeometryAttribute ToVertexUvwAttribute(this Vector3[] xs) => xs.ToIArray().ToVertexUvwAttribute(); - public static GeometryAttribute GetAttributeVertexUvw(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexUvw); - public static IArray GetAttributeDataVertexUvw(this IGeometryAttributes self) => self.GetAttributeVertexUvw()?.Data; - public static GeometryAttribute ToVertexNormalAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.VertexNormal, index); - public static GeometryAttribute ToVertexNormalAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.VertexNormal); - public static GeometryAttribute ToVertexNormalAttribute(this Vector3[] xs, int index) => xs.ToIArray().ToVertexNormalAttribute(index); - public static GeometryAttribute ToVertexNormalAttribute(this Vector3[] xs) => xs.ToIArray().ToVertexNormalAttribute(); - public static GeometryAttribute GetAttributeVertexNormal(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexNormal); - public static IArray GetAttributeDataVertexNormal(this IGeometryAttributes self) => self.GetAttributeVertexNormal()?.Data; - public static GeometryAttribute ToVertexColorAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.VertexColor, index); - public static GeometryAttribute ToVertexColorAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.VertexColor); - public static GeometryAttribute ToVertexColorAttribute(this Vector4[] xs, int index) => xs.ToIArray().ToVertexColorAttribute(index); - public static GeometryAttribute ToVertexColorAttribute(this Vector4[] xs) => xs.ToIArray().ToVertexColorAttribute(); - public static GeometryAttribute GetAttributeVertexColor(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexColor); - public static IArray GetAttributeDataVertexColor(this IGeometryAttributes self) => self.GetAttributeVertexColor()?.Data; - public static GeometryAttribute ToVertexColor8BitAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.VertexColor8Bit, index); - public static GeometryAttribute ToVertexColor8BitAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.VertexColor8Bit); - public static GeometryAttribute ToVertexColor8BitAttribute(this Byte4[] xs, int index) => xs.ToIArray().ToVertexColor8BitAttribute(index); - public static GeometryAttribute ToVertexColor8BitAttribute(this Byte4[] xs) => xs.ToIArray().ToVertexColor8BitAttribute(); - public static GeometryAttribute GetAttributeVertexColor8Bit(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexColor8Bit); - public static IArray GetAttributeDataVertexColor8Bit(this IGeometryAttributes self) => self.GetAttributeVertexColor8Bit()?.Data; - public static GeometryAttribute ToVertexBitangentAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.VertexBitangent, index); - public static GeometryAttribute ToVertexBitangentAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.VertexBitangent); - public static GeometryAttribute ToVertexBitangentAttribute(this Vector3[] xs, int index) => xs.ToIArray().ToVertexBitangentAttribute(index); - public static GeometryAttribute ToVertexBitangentAttribute(this Vector3[] xs) => xs.ToIArray().ToVertexBitangentAttribute(); - public static GeometryAttribute GetAttributeVertexBitangent(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexBitangent); - public static IArray GetAttributeDataVertexBitangent(this IGeometryAttributes self) => self.GetAttributeVertexBitangent()?.Data; - public static GeometryAttribute ToVertexTangentAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.VertexTangent, index); - public static GeometryAttribute ToVertexTangentAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.VertexTangent); - public static GeometryAttribute ToVertexTangentAttribute(this Vector4[] xs, int index) => xs.ToIArray().ToVertexTangentAttribute(index); - public static GeometryAttribute ToVertexTangentAttribute(this Vector4[] xs) => xs.ToIArray().ToVertexTangentAttribute(); - public static GeometryAttribute GetAttributeVertexTangent(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexTangent); - public static IArray GetAttributeDataVertexTangent(this IGeometryAttributes self) => self.GetAttributeVertexTangent()?.Data; - public static GeometryAttribute ToVertexSelectionWeightAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.VertexSelectionWeight, index); - public static GeometryAttribute ToVertexSelectionWeightAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.VertexSelectionWeight); - public static GeometryAttribute ToVertexSelectionWeightAttribute(this float[] xs, int index) => xs.ToIArray().ToVertexSelectionWeightAttribute(index); - public static GeometryAttribute ToVertexSelectionWeightAttribute(this float[] xs) => xs.ToIArray().ToVertexSelectionWeightAttribute(); - public static GeometryAttribute GetAttributeVertexSelectionWeight(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexSelectionWeight); - public static IArray GetAttributeDataVertexSelectionWeight(this IGeometryAttributes self) => self.GetAttributeVertexSelectionWeight()?.Data; - public static GeometryAttribute ToFaceColorAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.FaceColor, index); - public static GeometryAttribute ToFaceColorAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.FaceColor); - public static GeometryAttribute ToFaceColorAttribute(this Vector4[] xs, int index) => xs.ToIArray().ToFaceColorAttribute(index); - public static GeometryAttribute ToFaceColorAttribute(this Vector4[] xs) => xs.ToIArray().ToFaceColorAttribute(); - public static GeometryAttribute GetAttributeFaceColor(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.FaceColor); - public static IArray GetAttributeDataFaceColor(this IGeometryAttributes self) => self.GetAttributeFaceColor()?.Data; - public static GeometryAttribute ToFaceMaterialAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.FaceMaterial, index); - public static GeometryAttribute ToFaceMaterialAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.FaceMaterial); - public static GeometryAttribute ToFaceMaterialAttribute(this int[] xs, int index) => xs.ToIArray().ToFaceMaterialAttribute(index); - public static GeometryAttribute ToFaceMaterialAttribute(this int[] xs) => xs.ToIArray().ToFaceMaterialAttribute(); - public static GeometryAttribute GetAttributeFaceMaterial(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.FaceMaterial); - public static IArray GetAttributeDataFaceMaterial(this IGeometryAttributes self) => self.GetAttributeFaceMaterial()?.Data; - public static GeometryAttribute ToFaceNormalAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.FaceNormal, index); - public static GeometryAttribute ToFaceNormalAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.FaceNormal); - public static GeometryAttribute ToFaceNormalAttribute(this Vector3[] xs, int index) => xs.ToIArray().ToFaceNormalAttribute(index); - public static GeometryAttribute ToFaceNormalAttribute(this Vector3[] xs) => xs.ToIArray().ToFaceNormalAttribute(); - public static GeometryAttribute GetAttributeFaceNormal(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.FaceNormal); - public static IArray GetAttributeDataFaceNormal(this IGeometryAttributes self) => self.GetAttributeFaceNormal()?.Data; - public static GeometryAttribute ToMeshSubmeshOffsetAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.MeshSubmeshOffset, index); - public static GeometryAttribute ToMeshSubmeshOffsetAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.MeshSubmeshOffset); - public static GeometryAttribute ToMeshSubmeshOffsetAttribute(this int[] xs, int index) => xs.ToIArray().ToMeshSubmeshOffsetAttribute(index); - public static GeometryAttribute ToMeshSubmeshOffsetAttribute(this int[] xs) => xs.ToIArray().ToMeshSubmeshOffsetAttribute(); - public static GeometryAttribute GetAttributeMeshSubmeshOffset(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.MeshSubmeshOffset); - public static IArray GetAttributeDataMeshSubmeshOffset(this IGeometryAttributes self) => self.GetAttributeMeshSubmeshOffset()?.Data; - public static GeometryAttribute ToInstanceTransformAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.InstanceTransform, index); - public static GeometryAttribute ToInstanceTransformAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.InstanceTransform); - public static GeometryAttribute ToInstanceTransformAttribute(this Matrix4x4[] xs, int index) => xs.ToIArray().ToInstanceTransformAttribute(index); - public static GeometryAttribute ToInstanceTransformAttribute(this Matrix4x4[] xs) => xs.ToIArray().ToInstanceTransformAttribute(); - public static GeometryAttribute GetAttributeInstanceTransform(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.InstanceTransform); - public static IArray GetAttributeDataInstanceTransform(this IGeometryAttributes self) => self.GetAttributeInstanceTransform()?.Data; - public static GeometryAttribute ToInstanceParentAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.InstanceParent, index); - public static GeometryAttribute ToInstanceParentAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.InstanceParent); - public static GeometryAttribute ToInstanceParentAttribute(this int[] xs, int index) => xs.ToIArray().ToInstanceParentAttribute(index); - public static GeometryAttribute ToInstanceParentAttribute(this int[] xs) => xs.ToIArray().ToInstanceParentAttribute(); - public static GeometryAttribute GetAttributeInstanceParent(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.InstanceParent); - public static IArray GetAttributeDataInstanceParent(this IGeometryAttributes self) => self.GetAttributeInstanceParent()?.Data; - public static GeometryAttribute ToInstanceMeshAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.InstanceMesh, index); - public static GeometryAttribute ToInstanceMeshAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.InstanceMesh); - public static GeometryAttribute ToInstanceMeshAttribute(this int[] xs, int index) => xs.ToIArray().ToInstanceMeshAttribute(index); - public static GeometryAttribute ToInstanceMeshAttribute(this int[] xs) => xs.ToIArray().ToInstanceMeshAttribute(); - public static GeometryAttribute GetAttributeInstanceMesh(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.InstanceMesh); - public static IArray GetAttributeDataInstanceMesh(this IGeometryAttributes self) => self.GetAttributeInstanceMesh()?.Data; - public static GeometryAttribute ToInstanceFlagsAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.InstanceFlags, index); - public static GeometryAttribute ToInstanceFlagsAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.InstanceFlags); - public static GeometryAttribute ToInstanceFlagsAttribute(this ushort[] xs, int index) => xs.ToIArray().ToInstanceFlagsAttribute(index); - public static GeometryAttribute ToInstanceFlagsAttribute(this ushort[] xs) => xs.ToIArray().ToInstanceFlagsAttribute(); - public static GeometryAttribute GetAttributeInstanceFlags(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.InstanceFlags); - public static IArray GetAttributeDataInstanceFlags(this IGeometryAttributes self) => self.GetAttributeInstanceFlags()?.Data; - public static GeometryAttribute ToLineTangentInAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.LineTangentIn, index); - public static GeometryAttribute ToLineTangentInAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.LineTangentIn); - public static GeometryAttribute ToLineTangentInAttribute(this Vector3[] xs, int index) => xs.ToIArray().ToLineTangentInAttribute(index); - public static GeometryAttribute ToLineTangentInAttribute(this Vector3[] xs) => xs.ToIArray().ToLineTangentInAttribute(); - public static GeometryAttribute GetAttributeLineTangentIn(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.LineTangentIn); - public static IArray GetAttributeDataLineTangentIn(this IGeometryAttributes self) => self.GetAttributeLineTangentIn()?.Data; - public static GeometryAttribute ToLineTangentOutAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.LineTangentOut, index); - public static GeometryAttribute ToLineTangentOutAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.LineTangentOut); - public static GeometryAttribute ToLineTangentOutAttribute(this Vector3[] xs, int index) => xs.ToIArray().ToLineTangentOutAttribute(index); - public static GeometryAttribute ToLineTangentOutAttribute(this Vector3[] xs) => xs.ToIArray().ToLineTangentOutAttribute(); - public static GeometryAttribute GetAttributeLineTangentOut(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.LineTangentOut); - public static IArray GetAttributeDataLineTangentOut(this IGeometryAttributes self) => self.GetAttributeLineTangentOut()?.Data; - public static GeometryAttribute ToShapeVertexAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.ShapeVertex, index); - public static GeometryAttribute ToShapeVertexAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.ShapeVertex); - public static GeometryAttribute ToShapeVertexAttribute(this Vector3[] xs, int index) => xs.ToIArray().ToShapeVertexAttribute(index); - public static GeometryAttribute ToShapeVertexAttribute(this Vector3[] xs) => xs.ToIArray().ToShapeVertexAttribute(); - public static GeometryAttribute GetAttributeShapeVertex(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.ShapeVertex); - public static IArray GetAttributeDataShapeVertex(this IGeometryAttributes self) => self.GetAttributeShapeVertex()?.Data; - public static GeometryAttribute ToShapeVertexOffsetAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.ShapeVertexOffset, index); - public static GeometryAttribute ToShapeVertexOffsetAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.ShapeVertexOffset); - public static GeometryAttribute ToShapeVertexOffsetAttribute(this int[] xs, int index) => xs.ToIArray().ToShapeVertexOffsetAttribute(index); - public static GeometryAttribute ToShapeVertexOffsetAttribute(this int[] xs) => xs.ToIArray().ToShapeVertexOffsetAttribute(); - public static GeometryAttribute GetAttributeShapeVertexOffset(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.ShapeVertexOffset); - public static IArray GetAttributeDataShapeVertexOffset(this IGeometryAttributes self) => self.GetAttributeShapeVertexOffset()?.Data; - public static GeometryAttribute ToShapeColorAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.ShapeColor, index); - public static GeometryAttribute ToShapeColorAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.ShapeColor); - public static GeometryAttribute ToShapeColorAttribute(this Vector4[] xs, int index) => xs.ToIArray().ToShapeColorAttribute(index); - public static GeometryAttribute ToShapeColorAttribute(this Vector4[] xs) => xs.ToIArray().ToShapeColorAttribute(); - public static GeometryAttribute GetAttributeShapeColor(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.ShapeColor); - public static IArray GetAttributeDataShapeColor(this IGeometryAttributes self) => self.GetAttributeShapeColor()?.Data; - public static GeometryAttribute ToShapeWidthAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.ShapeWidth, index); - public static GeometryAttribute ToShapeWidthAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.ShapeWidth); - public static GeometryAttribute ToShapeWidthAttribute(this float[] xs, int index) => xs.ToIArray().ToShapeWidthAttribute(index); - public static GeometryAttribute ToShapeWidthAttribute(this float[] xs) => xs.ToIArray().ToShapeWidthAttribute(); - public static GeometryAttribute GetAttributeShapeWidth(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.ShapeWidth); - public static IArray GetAttributeDataShapeWidth(this IGeometryAttributes self) => self.GetAttributeShapeWidth()?.Data; - public static GeometryAttribute ToMaterialColorAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.MaterialColor, index); - public static GeometryAttribute ToMaterialColorAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.MaterialColor); - public static GeometryAttribute ToMaterialColorAttribute(this Vector4[] xs, int index) => xs.ToIArray().ToMaterialColorAttribute(index); - public static GeometryAttribute ToMaterialColorAttribute(this Vector4[] xs) => xs.ToIArray().ToMaterialColorAttribute(); - public static GeometryAttribute GetAttributeMaterialColor(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.MaterialColor); - public static IArray GetAttributeDataMaterialColor(this IGeometryAttributes self) => self.GetAttributeMaterialColor()?.Data; - public static GeometryAttribute ToMaterialGlossinessAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.MaterialGlossiness, index); - public static GeometryAttribute ToMaterialGlossinessAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.MaterialGlossiness); - public static GeometryAttribute ToMaterialGlossinessAttribute(this float[] xs, int index) => xs.ToIArray().ToMaterialGlossinessAttribute(index); - public static GeometryAttribute ToMaterialGlossinessAttribute(this float[] xs) => xs.ToIArray().ToMaterialGlossinessAttribute(); - public static GeometryAttribute GetAttributeMaterialGlossiness(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.MaterialGlossiness); - public static IArray GetAttributeDataMaterialGlossiness(this IGeometryAttributes self) => self.GetAttributeMaterialGlossiness()?.Data; - public static GeometryAttribute ToMaterialSmoothnessAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.MaterialSmoothness, index); - public static GeometryAttribute ToMaterialSmoothnessAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.MaterialSmoothness); - public static GeometryAttribute ToMaterialSmoothnessAttribute(this float[] xs, int index) => xs.ToIArray().ToMaterialSmoothnessAttribute(index); - public static GeometryAttribute ToMaterialSmoothnessAttribute(this float[] xs) => xs.ToIArray().ToMaterialSmoothnessAttribute(); - public static GeometryAttribute GetAttributeMaterialSmoothness(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.MaterialSmoothness); - public static IArray GetAttributeDataMaterialSmoothness(this IGeometryAttributes self) => self.GetAttributeMaterialSmoothness()?.Data; - public static GeometryAttribute ToSubmeshIndexOffsetAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.SubmeshIndexOffset, index); - public static GeometryAttribute ToSubmeshIndexOffsetAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.SubmeshIndexOffset); - public static GeometryAttribute ToSubmeshIndexOffsetAttribute(this int[] xs, int index) => xs.ToIArray().ToSubmeshIndexOffsetAttribute(index); - public static GeometryAttribute ToSubmeshIndexOffsetAttribute(this int[] xs) => xs.ToIArray().ToSubmeshIndexOffsetAttribute(); - public static GeometryAttribute GetAttributeSubmeshIndexOffset(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.SubmeshIndexOffset); - public static IArray GetAttributeDataSubmeshIndexOffset(this IGeometryAttributes self) => self.GetAttributeSubmeshIndexOffset()?.Data; - public static GeometryAttribute ToSubmeshMaterialAttribute(this IArray xs, int index) => xs.ToAttribute(CommonAttributes.SubmeshMaterial, index); - public static GeometryAttribute ToSubmeshMaterialAttribute(this IArray xs) => xs.ToAttribute(CommonAttributes.SubmeshMaterial); - public static GeometryAttribute ToSubmeshMaterialAttribute(this int[] xs, int index) => xs.ToIArray().ToSubmeshMaterialAttribute(index); - public static GeometryAttribute ToSubmeshMaterialAttribute(this int[] xs) => xs.ToIArray().ToSubmeshMaterialAttribute(); - public static GeometryAttribute GetAttributeSubmeshMaterial(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.SubmeshMaterial); - public static IArray GetAttributeDataSubmeshMaterial(this IGeometryAttributes self) => self.GetAttributeSubmeshMaterial()?.Data; - - } -} diff --git a/src/cs/g3d/Vim.G3d/CommonAttributes.tt b/src/cs/g3d/Vim.G3d/CommonAttributes.tt deleted file mode 100644 index fad9970c..00000000 --- a/src/cs/g3d/Vim.G3d/CommonAttributes.tt +++ /dev/null @@ -1,95 +0,0 @@ -<#@ template language="C#" #> -<#@ output extension=".cs" #> -<#@ assembly name="System.Core" #> -<#@ import namespace="System.Linq" #> -<#@ import namespace="System.Text" #> -<#@ import namespace="System.Collections.Generic" #> - -// AUTOGENERATED FILE: DO NOT EDIT -// This file is generated from CommonAttributeExtensions.tt - -<# -string[] namesDescriptorAndTypes = { - "ObjectFaceSize", "g3d:all:facesize:0:int32:1","int", - "Index", "g3d:corner:index:0:int32:1","int", - "Position", "g3d:vertex:position:0:float32:3","Vector3", - "VertexUv", "g3d:vertex:uv:0:float32:2","Vector2", - "VertexUvw", "g3d:vertex:uv:0:float32:3","Vector3", - "VertexNormal", "g3d:vertex:normal:0:float32:3","Vector3", - "VertexColor", "g3d:vertex:color:0:float32:4","Vector4", - "VertexColor8Bit", "g3d:vertex:color:0:int8:4","Byte4", - "VertexBitangent", "g3d:vertex:bitangent:0:float32:3","Vector3", - "VertexTangent", "g3d:vertex:tangent:0:float32:4","Vector4", - "VertexSelectionWeight", "g3d:vertex:weight:0:float32:1","float", - "FaceColor", "g3d:face:color:0:float32:4","Vector4", - "FaceMaterial", "g3d:face:material:0:int32:1","int", - "FaceNormal", "g3d:face:normal:0:float32:3","Vector3", - - "MeshSubmeshOffset", "g3d:mesh:submeshoffset:0:int32:1", "int", - - "InstanceTransform", "g3d:instance:transform:0:float32:16", "Matrix4x4", - "InstanceParent", "g3d:instance:parent:0:int32:1", "int", - "InstanceMesh", "g3d:instance:mesh:0:int32:1", "int", - "InstanceFlags", "g3d:instance:flags:0:uint16:1", "ushort", - - "LineTangentIn", "g3d:vertex:tangent:0:float32:3","Vector3", - "LineTangentOut", "g3d:vertex:tangent:1:float32:3","Vector3", - "ShapeVertex", "g3d:shapevertex:position:0:float32:3", "Vector3", // We're using a distinct "shapevertex" association here because the "vertex" association is coupled to mesh geometry (there is a lot of logic related to face remapping and merging). - "ShapeVertexOffset", "g3d:shape:vertexoffset:0:int32:1", "int", - "ShapeColor", "g3d:shape:color:0:float32:4", "Vector4", - "ShapeWidth", "g3d:shape:width:0:float32:1", "float", - - "MaterialColor", "g3d:material:color:0:float32:4","Vector4", - "MaterialGlossiness", "g3d:material:glossiness:0:float32:1","float", - "MaterialSmoothness", "g3d:material:smoothness:0:float32:1","float", - - "SubmeshIndexOffset", "g3d:submesh:indexoffset:0:int32:1","int", - "SubmeshMaterial", "g3d:submesh:material:0:int32:1","int", - -}; -#> - -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.G3d -{ - public static class CommonAttributes - { - -<# - for (var i=0; i < namesDescriptorAndTypes.Length; i += 3) - { - var name = namesDescriptorAndTypes[i]; - var desc = namesDescriptorAndTypes[i + 1]; - var type = namesDescriptorAndTypes[i + 2]; -#> - public const string <#= name #> = "<#= desc #>"; -<# - } -#> - } - - public static class CommonAttributeExtensions - { - -<# - for (var i=0; i < namesDescriptorAndTypes.Length; i += 3) - { - var name = namesDescriptorAndTypes[i]; - var desc = namesDescriptorAndTypes[i + 1]; - var type = namesDescriptorAndTypes[i + 2]; - var codeName = "CommonAttributes." + name; -#> - public static GeometryAttribute<<#= type #>> To<#= name #>Attribute(this IArray<<#= type #>> xs, int index) => xs.ToAttribute(<#= codeName #>, index); - public static GeometryAttribute<<#= type #>> To<#= name #>Attribute(this IArray<<#= type #>> xs) => xs.ToAttribute(<#= codeName #>); - public static GeometryAttribute<<#= type #>> To<#= name #>Attribute(this <#= type #>[] xs, int index) => xs.ToIArray().To<#= name #>Attribute(index); - public static GeometryAttribute<<#= type #>> To<#= name #>Attribute(this <#= type #>[] xs) => xs.ToIArray().To<#= name #>Attribute(); - public static GeometryAttribute<<#= type #>> GetAttribute<#= name #>(this IGeometryAttributes self) => self.GetAttribute<<#= type #>>(<#= codeName #>); - public static IArray<<#= type #>> GetAttributeData<#= name #>(this IGeometryAttributes self) => self.GetAttribute<#= name #>()?.Data; -<# - } -#> - - } -} diff --git a/src/cs/g3d/Vim.G3d/Constants.cs b/src/cs/g3d/Vim.G3d/Constants.cs new file mode 100644 index 00000000..d22e623b --- /dev/null +++ b/src/cs/g3d/Vim.G3d/Constants.cs @@ -0,0 +1,86 @@ +namespace Vim.G3d +{ + /// + /// Defines method for additionnal setup after constructors in generated G3d classes. + /// + public interface ISetup + { + void Setup(); + } + + public enum MeshSection + { + Opaque, + Transparent, + All + } + public static class CommonAttributes + { + public const string ObjectFaceSize = "g3d:all:facesize:0:int32:1"; + public const string Index = "g3d:corner:index:0:int32:1"; + public const string Position = "g3d:vertex:position:0:float32:3"; + public const string VertexUv = "g3d:vertex:uv:0:float32:2"; + public const string VertexUvw = "g3d:vertex:uv:0:float32:3"; + public const string VertexNormal = "g3d:vertex:normal:0:float32:3"; + public const string VertexColor = "g3d:vertex:color:0:float32:4"; + public const string VertexColor8Bit = "g3d:vertex:color:0:int8:4"; + public const string VertexBitangent = "g3d:vertex:bitangent:0:float32:3"; + public const string VertexTangent = "g3d:vertex:tangent:0:float32:4"; + public const string VertexSelectionWeight = "g3d:vertex:weight:0:float32:1"; + public const string FaceColor = "g3d:face:color:0:float32:4"; + public const string FaceMaterial = "g3d:face:material:0:int32:1"; + public const string FaceNormal = "g3d:face:normal:0:float32:3"; + public const string MeshSubmeshOffset = "g3d:mesh:submeshoffset:0:int32:1"; + public const string InstanceTransform = "g3d:instance:transform:0:float32:16"; + public const string InstanceParent = "g3d:instance:parent:0:int32:1"; + public const string InstanceMesh = "g3d:instance:mesh:0:int32:1"; + public const string InstanceFlags = "g3d:instance:flags:0:uint16:1"; + public const string LineTangentIn = "g3d:vertex:tangent:0:float32:3"; + public const string LineTangentOut = "g3d:vertex:tangent:1:float32:3"; + public const string ShapeVertex = "g3d:shapevertex:position:0:float32:3"; + public const string ShapeVertexOffset = "g3d:shape:vertexoffset:0:int32:1"; + public const string ShapeColor = "g3d:shape:color:0:float32:4"; + public const string ShapeWidth = "g3d:shape:width:0:float32:1"; + public const string MaterialColor = "g3d:material:color:0:float32:4"; + public const string MaterialGlossiness = "g3d:material:glossiness:0:float32:1"; + public const string MaterialSmoothness = "g3d:material:smoothness:0:float32:1"; + public const string SubmeshIndexOffset = "g3d:submesh:indexoffset:0:int32:1"; + public const string SubmeshMaterial = "g3d:submesh:material:0:int32:1"; + } + + public static class Utils { + public static bool SafeEqual(this T[] a, T[] b) + { + if (a == null && b == null) return true; + if (a == null) return false; + if(b == null) return false; + if(a.Length != b.Length) return false; + for(var i= 0; i < a.Length; i++) + { + if (!a[i].Equals(b[i])) return false; + } + return true; + } + + public static T SafeGet(this T[] a, int i) where T : class + { + if (i < 0) return null; + if (i >= a.Length) return null; + return a[i]; + } + } + + public static class Constants + { + public const string G3dPrefix = "g3d"; + public const string Separator = ":"; + public const char SeparatorChar = ':'; + + public const string MetaHeaderSegmentName = "meta"; + public const long MetaHeaderSegmentNumBytes = 8; // The header is 7 bytes + 1 bytes padding. + public const byte MetaHeaderMagicA = 0x63; + public const byte MetaHeaderMagicB = 0xD0; + + public static readonly string[] MetaHeaderSupportedUnits = { "mm", "cm", "m\0", "km", "in", "ft", "yd", "mi" }; + } +} diff --git a/src/cs/g3d/Vim.G3d/Enums.cs b/src/cs/g3d/Vim.G3d/Enums.cs deleted file mode 100644 index a3e1db1f..00000000 --- a/src/cs/g3d/Vim.G3d/Enums.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; - -namespace Vim.G3d -{ - /// - /// The different types of data types that can be used as elements. - /// - public enum DataType - { - dt_uint8, - dt_int8, - dt_uint16, - dt_int16, - dt_uint32, - dt_int32, - dt_uint64, - dt_int64, - dt_float32, - dt_float64, - dt_string, - }; - - /// - /// What element or component of a mesh each attribute is associated with. - /// - public enum Association - { - assoc_all, // associated with all data in G3d - assoc_none, // no association - assoc_vertex, // vertex or point data - assoc_face, // face associated data - assoc_corner, // corner (aka face-vertex) data. A corner is associated with one vertex, but a vertex may be shared between multiple corners - assoc_edge, // half-edge data. Each face consists of n half-edges, one per corner. A half-edge, is a directed edge - assoc_instance, // instance information - assoc_shapevertex, // flattened shape vertex collection. - assoc_shape, // shape instance - assoc_material, // material properties - assoc_mesh, - assoc_submesh - } - - [Flags] - public enum InstanceFlags - { - /// - /// Default - no instance options defined. - /// - None = 0, - - /// - /// When enabled, indicates that the renderer (or the consuming application) should hide - /// the instance by default. - /// - Hidden = 1, - } - - /// - /// Common semantic names. - /// - public static class Semantic - { - public const string Position = "position"; - public const string Index = "index"; - public const string FaceSize = "facesize"; - public const string Uv = "uv"; - public const string Normal = "normal"; - public const string Color = "color"; - public const string Bitangent = "bitangent"; - public const string Tangent = "tangent"; - public const string Weight = "weight"; - public const string Width = "width"; - - // Usually associated with face. - public const string Material = "material"; - - // Usually associated with material. - public const string Glossiness = "glossiness"; - - public const string Smoothness = "smoothness"; - - // Usually associated with meshes and submeshes - public const string IndexOffset = "indexoffset"; - public const string VertexOffset = "vertexoffset"; - - // Usually associated with instances - public const string Subgeometry = "subgeometry"; - public const string Mesh = "mesh"; - public const string Parent = "parent"; - public const string Transform = "transform"; - public const string Flags = "flags"; - - public const string TangentInt = "tangentin"; - public const string TangentOut = "tangentout"; - - public const string SubMeshOffset = "submeshoffset"; - - } -} diff --git a/src/cs/g3d/Vim.G3d/G3D.cs b/src/cs/g3d/Vim.G3d/G3D.cs deleted file mode 100644 index 98aed23b..00000000 --- a/src/cs/g3d/Vim.G3d/G3D.cs +++ /dev/null @@ -1,314 +0,0 @@ -/* - G3D Geometry Format Library - Copyright 2019, VIMaec LLC. - Copyright 2018, Ara 3D Inc. - Usage licensed under terms of MIT License -*/ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.G3d -{ - /// - /// Represents a basic single-precision G3D in memory, with access to common attributes. - /// The G3D format can be double precision, but this data structure won't provide access to all of the attributes. - /// In the case of G3D formats that are non-conformant to the expected semantics you can use GeometryAttributes. - /// This class is inspired heavily by the structure of FBX and Assimp. - /// - public class G3D : GeometryAttributes - { - public new static G3D Empty = Create(); - - public G3dHeader Header { get; } - - // These are the values of the most common attributes. Some are retrieved directly from data, others are computed on demand, or coerced. - - // Vertex buffer. Usually present. - public IArray Vertices { get; } - - // Index buffer (one index per corner, and per half-edge). Computed if absent. - public IArray Indices { get; } - - // Vertex associated data, provided or null - public List> AllVertexUvs { get; } = new List>(); - public List> AllVertexColors { get; } = new List>(); - public IArray VertexUvs => AllVertexUvs?.ElementAtOrDefault(0); - public IArray VertexColors => AllVertexColors?.ElementAtOrDefault(0); - public IArray VertexNormals { get; } - public IArray VertexTangents { get; } - - // Faces - public IArray FaceMaterials { get; } // Material indices per face, - public IArray FaceNormals { get; } // If not provided, are computed dynamically as the average of all vertex normals, - - // Meshes - public IArray MeshIndexOffsets { get; } // Offset into the index buffer for each Mesh - public IArray MeshVertexOffsets { get; } // Offset into the vertex buffer for each Mesh - public IArray MeshIndexCounts { get; } // Computed - public IArray MeshVertexCounts { get; } // Computed - public IArray MeshSubmeshOffset { get; } - public IArray MeshSubmeshCount { get; } // Computed - public IArray Meshes { get; } - - // Instances - public IArray InstanceParents { get; } // Index of the parent transform - public IArray InstanceTransforms { get; } // A 4x4 matrix in row-column order defining the transormed - public IArray InstanceMeshes { get; } // The SubGeometry associated with the index - public IArray InstanceFlags { get; } // The instance flags associated with the index. - - // Shapes - public IArray ShapeVertices { get; } - public IArray ShapeVertexOffsets { get; } - public IArray ShapeColors { get; } - public IArray ShapeWidths { get; } - public IArray ShapeVertexCounts { get; } // Computed - public IArray Shapes { get; } // Computed - - // Materials - public IArray MaterialColors { get; } // RGBA with transparency. - public IArray MaterialGlossiness { get; } - public IArray MaterialSmoothness { get; } - public IArray Materials { get; } - - - // Submeshes - public IArray SubmeshIndexOffsets { get; } - public IArray SubmeshIndexCount { get; } - public IArray SubmeshMaterials { get; } - - public G3D(IEnumerable attributes, G3dHeader? header = null, int numCornersPerFaceOverride = -1) - : base(attributes, numCornersPerFaceOverride) - { - Header = header ?? new G3dHeader(); - - foreach (var attr in Attributes.ToEnumerable()) - { - var desc = attr.Descriptor; - switch (desc.Semantic) - { - case Semantic.Index: - if (attr.IsTypeAndAssociation(Association.assoc_corner)) - Indices = Indices ?? attr.AsType().Data; - if (attr.IsTypeAndAssociation(Association.assoc_corner)) - Indices = Indices ?? attr.AsType().Data.Select(x => (int)x); - break; - - case Semantic.Position: - if (attr.IsTypeAndAssociation(Association.assoc_vertex)) - Vertices = Vertices ?? attr.AsType().Data; - if (attr.IsTypeAndAssociation(Association.assoc_corner)) - Vertices = Vertices ?? attr.AsType().Data; // TODO: is this used? - if (attr.IsTypeAndAssociation(Association.assoc_shapevertex)) - ShapeVertices = ShapeVertices ?? attr.AsType().Data; - break; - - case Semantic.Tangent: - if (attr.IsTypeAndAssociation(Association.assoc_vertex)) - VertexTangents = VertexTangents ?? attr.AsType().Data.Select(v => v.ToVector4()); - if (attr.IsTypeAndAssociation(Association.assoc_vertex)) - VertexTangents = VertexTangents ?? attr.AsType().Data; - break; - - case Semantic.Uv: - if (attr.IsTypeAndAssociation(Association.assoc_vertex)) - AllVertexUvs.Add(attr.AsType().Data.Select(uv => uv.ToVector2())); - if (attr.IsTypeAndAssociation(Association.assoc_vertex)) - AllVertexUvs.Add(attr.AsType().Data); - break; - - case Semantic.Color: - if (desc.Association == Association.assoc_vertex) - AllVertexColors.Add(attr.AttributeToColors()); - if (desc.Association == Association.assoc_shape) - ShapeColors = ShapeColors ?? attr.AttributeToColors(); - if (desc.Association == Association.assoc_material) - MaterialColors = MaterialColors ?? attr.AttributeToColors(); - break; - - case Semantic.VertexOffset: - if (attr.IsTypeAndAssociation(Association.assoc_mesh)) - MeshVertexOffsets = MeshVertexOffsets ?? attr.AsType().Data; - if (attr.IsTypeAndAssociation(Association.assoc_shape)) - ShapeVertexOffsets = ShapeVertexOffsets ?? attr.AsType().Data; - break; - - case Semantic.IndexOffset: - if (attr.IsTypeAndAssociation(Association.assoc_mesh)) - MeshIndexOffsets = MeshIndexOffsets ?? attr.AsType().Data; - if (attr.IsTypeAndAssociation(Association.assoc_submesh)) - SubmeshIndexOffsets = SubmeshIndexOffsets ?? attr.AsType().Data; - break; - - case Semantic.Normal: - if (attr.IsTypeAndAssociation(Association.assoc_face)) - FaceNormals = FaceNormals ?? attr.AsType().Data; - if (attr.IsTypeAndAssociation(Association.assoc_vertex)) - VertexNormals = VertexNormals ?? attr.AsType().Data; - break; - - case Semantic.Material: - if (attr.IsTypeAndAssociation(Association.assoc_face)) - FaceMaterials = FaceMaterials ?? attr.AsType().Data; - if (attr.IsTypeAndAssociation(Association.assoc_submesh)) - SubmeshMaterials = SubmeshMaterials ?? attr.AsType().Data; - break; - - case Semantic.Parent: - if (attr.IsTypeAndAssociation(Association.assoc_instance)) - InstanceParents = InstanceParents ?? attr.AsType().Data; - break; - - case Semantic.Mesh: - if (attr.IsTypeAndAssociation(Association.assoc_instance)) - InstanceMeshes = InstanceMeshes ?? attr.AsType().Data; - break; - - case Semantic.Transform: - if (attr.IsTypeAndAssociation(Association.assoc_instance)) - InstanceTransforms = InstanceTransforms ?? attr.AsType().Data; - break; - - case Semantic.Width: - if (attr.IsTypeAndAssociation(Association.assoc_shape)) - ShapeWidths = ShapeWidths ?? attr.AsType().Data; - break; - - case Semantic.Glossiness: - if (attr.IsTypeAndAssociation(Association.assoc_material)) - MaterialGlossiness = attr.AsType().Data; - break; - - case Semantic.Smoothness: - if (attr.IsTypeAndAssociation(Association.assoc_material)) - MaterialSmoothness = attr.AsType().Data; - break; - - case Semantic.SubMeshOffset: - if (attr.IsTypeAndAssociation(Association.assoc_mesh)) - MeshSubmeshOffset = attr.AsType().Data; - break; - - case Semantic.Flags: - if (attr.IsTypeAndAssociation(Association.assoc_instance)) - InstanceFlags = attr.AsType().Data; - break; - } - } - - // If no vertices are provided, we are going to generate a list of zero vertices. - if (Vertices == null) - Vertices = Vector3.Zero.Repeat(0); - - // If no indices are provided then we are going to have to treat the index buffer as indices - if (Indices == null) - Indices = Vertices.Indices(); - - // Compute face normals if possible - if (FaceNormals == null && VertexNormals != null) - FaceNormals = NumFaces.Select(ComputeFaceNormal); - - if (NumMeshes > 0) - { - // Mesh offset is the same as the offset of its first submesh. - if(MeshSubmeshOffset != null) - { - MeshIndexOffsets = MeshSubmeshOffset.Select(submesh => SubmeshIndexOffsets[submesh]); - MeshSubmeshCount = GetSubArrayCounts(MeshSubmeshOffset.Count, MeshSubmeshOffset, NumSubmeshes).Evaluate(); - } - - if(MeshIndexOffsets != null) - { - MeshIndexCounts = GetSubArrayCounts(NumMeshes, MeshIndexOffsets, NumCorners); - MeshVertexOffsets = MeshIndexOffsets - .Zip(MeshIndexCounts, (start, count) => (start, count)) - .Select(range => Indices.SubArray(range.start, range.count).Min()); - } - - if (MeshVertexOffsets != null) - MeshVertexCounts = GetSubArrayCounts(NumMeshes, MeshVertexOffsets, NumVertices); - } - else - { - MeshSubmeshCount = Array.Empty().ToIArray(); - } - - if (SubmeshIndexOffsets != null) - SubmeshIndexCount = GetSubArrayCounts(SubmeshIndexOffsets.Count, SubmeshIndexOffsets, NumCorners).Evaluate(); - - // Compute all meshes - Meshes = NumMeshes.Select(i => new G3dMesh(this, i)); - - if (MaterialColors != null) - Materials = MaterialColors.Count.Select(i => new G3dMaterial(this, i)); - - // Process the shape data - if (ShapeVertices == null) - ShapeVertices = Vector3.Zero.Repeat(0); - - if (ShapeVertexOffsets == null) - ShapeVertexOffsets = Array.Empty().ToIArray(); - - if (ShapeColors == null) - ShapeColors = Vector4.Zero.Repeat(0); - - if (ShapeWidths == null) - ShapeWidths = Array.Empty().ToIArray(); - - // Update the instance options - if (InstanceFlags == null) - InstanceFlags = ((ushort) 0).Repeat(NumInstances); - - ShapeVertexCounts = GetSubArrayCounts(NumShapes, ShapeVertexOffsets, ShapeVertices.Count); - ValidateSubArrayCounts(ShapeVertexCounts, nameof(ShapeVertexCounts)); - - Shapes = NumShapes.Select(i => new G3dShape(this, i)); - } - - private static IArray GetSubArrayCounts(int numItems, IArray offsets, int totalCount) - => numItems.Select(i => i < (numItems - 1) - ? offsets[i + 1] - offsets[i] - : totalCount - offsets[i]); - - private static void ValidateSubArrayCounts(IArray subArrayCounts, string memberName) - { - for (var i = 0; i < subArrayCounts.Count; ++i) - { - if (subArrayCounts[i] < 0) - throw new Exception($"{memberName}[{i}] is a negative sub array count."); - } - } - - public static Vector3 Average(IArray xs) - => xs.Aggregate(Vector3.Zero, (a, b) => a + b) / xs.Count; - - public Vector3 ComputeFaceNormal(int nFace) - => Average(NumCornersPerFace.Select(c => VertexNormals[nFace * NumCornersPerFace + c])); - - public static G3D Read(string filePath) - { - using (var stream = File.OpenRead(filePath)) - return stream.ReadG3d(); - } - - public static G3D Read(Stream stream) - => stream.ReadG3d(); - - public static G3D Read(byte[] bytes) - { - using (var stream = new MemoryStream(bytes)) - return stream.ReadG3d(); - } - - public static G3D Create(params GeometryAttribute[] attributes) - => new G3D(attributes); - - public static G3D Create(G3dHeader header, params GeometryAttribute[] attributes) - => new G3D(attributes, header); - } -} diff --git a/src/cs/g3d/Vim.G3d/G3DBuilder.cs b/src/cs/g3d/Vim.G3d/G3DBuilder.cs deleted file mode 100644 index 6d68cb99..00000000 --- a/src/cs/g3d/Vim.G3d/G3DBuilder.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.G3d -{ - /// - /// This is a helper class for constructing a G3D from individual attributes - /// - public class G3DBuilder - { - public readonly List Attributes = new List(); - - public G3D ToG3D(G3dHeader? header = null) - => new G3D(Attributes, header ?? G3dHeader.Default); - - public G3DBuilder Add(GeometryAttribute attr) - { - Attributes.Add(attr); - return this; - } - - public G3DBuilder AddIndices(int[] indices) - => Add(indices.ToIndexAttribute()); - - public G3DBuilder AddIndices(IArray indices) - => Add(indices.ToIndexAttribute()); - - public G3DBuilder SetObjectFaceSize(int objectFaceSize) - => Add(new[] { objectFaceSize }.ToIArray().ToObjectFaceSizeAttribute()); - - public G3DBuilder AddVertices(IArray vertices) - => Add(vertices.ToPositionAttribute()); - - public IGeometryAttributes ToIGeometryAttributes() - => new GeometryAttributes(Attributes); - } -} - diff --git a/src/cs/g3d/Vim.G3d/G3dChunk.cs b/src/cs/g3d/Vim.G3d/G3dChunk.cs new file mode 100644 index 00000000..c85d9bc9 --- /dev/null +++ b/src/cs/g3d/Vim.G3d/G3dChunk.cs @@ -0,0 +1,168 @@ +using System; +using System.Linq; +using Vim.Math3d; + +namespace Vim.G3d +{ + + + public partial class G3dChunk + { + void ISetup.Setup() + { + // empty + } + + + public int GetSubmeshCount() => SubmeshIndexOffsets?.Length ?? 0; + + public int getMeshCount() => MeshSubmeshOffset?.Length ?? 0; + + /// + /// The total number of submeshes. + /// + public int GetSubmeshCount(int mesh, MeshSection section) => + GetMeshSubmeshEnd(mesh, section) - GetMeshSubmeshStart(mesh, section); + + public int GetMeshSubmeshStart(int mesh, MeshSection section) + { + if (section == MeshSection.Opaque || section == MeshSection.All) + { + return MeshSubmeshOffset[mesh]; + } + + return MeshSubmeshOffset[mesh] + MeshOpaqueSubmeshCounts[mesh]; + } + + public int GetMeshSubmeshEnd(int mesh, MeshSection section) + { + if (section == MeshSection.Opaque) + { + return MeshSubmeshOffset[mesh] + MeshOpaqueSubmeshCounts[mesh]; + } + if(mesh + 1 >= MeshSubmeshOffset.Length) + { + return SubmeshIndexOffsets.Length; + } + return MeshSubmeshOffset[mesh + 1]; + } + + public int GetMeshIndexStart(int mesh, MeshSection section) + { + var sub = GetMeshSubmeshStart(mesh, section); + return GetSubmeshIndexStart(sub); + } + + public int GetMeshIndexEnd(int mesh, MeshSection section) + { + var sub = GetMeshSubmeshEnd(mesh, section); + return GetSubmeshIndexEnd(sub); + } + + public int GetMeshIndexCount(int mesh, MeshSection section) + { + return GetMeshIndexEnd(mesh, section) - GetMeshIndexStart(mesh, section); + } + + public AABox GetAABox(int mesh, Matrix4x4 matrix) + { + var start = GetMeshVertexStart(mesh, MeshSection.All); + var end = GetMeshVertexEnd(mesh, MeshSection.All); + var min = Positions[start].Transform(matrix); + var max = min; + for (var v = start + 1; v < end; v++) + { + var pos = Positions[v].Transform(matrix); + min = min.Min(pos); + max = max.Max(pos); + } + return new AABox(min, max); + } + + /// + /// The total number of indices. + /// + public int GetIndexCount() => Indices?.Length ?? 0; + + public int GetMeshVertexStart(int mesh, MeshSection section) + { + var sub = GetMeshSubmeshStart(mesh, section); + return GetSubmeshVertexStart(sub); + } + + public int GetMeshVertexEnd(int mesh, MeshSection section) + { + var sub = GetMeshSubmeshEnd(mesh, section) - 1; + return GetSubmeshVertexEnd(sub); + } + + public int GetMeshVertexCount(int mesh, MeshSection section) + { + return GetMeshVertexEnd(mesh, section) - GetMeshVertexStart(mesh, section); + } + + /// + /// The total number of vertices. + /// + public int GetVertexCount() => (Positions?.Length ?? 0); + + public int GetSubmeshIndexStart(int submesh) + { + return SubmeshIndexOffsets[submesh]; + } + + public int GetSubmeshIndexEnd(int submesh) + { + return submesh + 1 < GetSubmeshCount() + ? SubmeshIndexOffsets[submesh + 1] + : GetIndexCount(); + } + + public int GetSubmeshIndexCount(int submesh) + { + return GetSubmeshIndexEnd(submesh) - GetSubmeshIndexStart(submesh); + } + + public int GetSubmeshVertexStart(int submesh) + { + return SubmeshVertexOffsets[submesh]; + } + + public int GetSubmeshVertexEnd(int submesh) + { + return submesh + 1 < GetSubmeshCount() ? SubmeshVertexOffsets[submesh + 1] : GetVertexCount(); + } + + public int GetSubmeshVertexCount(int submesh) + { + return GetSubmeshVertexEnd(submesh) - GetSubmeshVertexStart(submesh); + } + + public AABox GetAABB() + { + var box = new AABox(Positions[0], Positions[0]); + for (var p = 1; p < Positions.Length; p++) + { + var pos = Positions[p]; + box = Expand(box, pos); + } + return box; + } + + static AABox Expand(AABox box, Vector3 pos) + { + return new AABox( + new Vector3( + Math.Min(box.Min.X, pos.X), + Math.Min(box.Min.Y, pos.Y), + Math.Min(box.Min.Z, pos.Z) + ), + new Vector3( + Math.Max(box.Max.X, pos.X), + Math.Max(box.Max.Y, pos.Y), + Math.Max(box.Max.Z, pos.Z) + ) + ); + } + } +} diff --git a/src/cs/g3d/Vim.G3d/G3dGenerated.g.cs b/src/cs/g3d/Vim.G3d/G3dGenerated.g.cs new file mode 100644 index 00000000..21c085f8 --- /dev/null +++ b/src/cs/g3d/Vim.G3d/G3dGenerated.g.cs @@ -0,0 +1,552 @@ +// AUTO-GENERATED FILE, DO NOT MODIFY. +// ReSharper disable All +using Vim.BFastLib; + +namespace Vim.G3d +{ + // Please provide an explicit implementation in another partial class file. + public partial class G3dVim : ISetup + { + public System.Int32[] Indices; + public Vim.Math3d.Vector3[] Positions; + public Vim.Math3d.Matrix4x4[] InstanceTransforms; + public System.Int32[] InstanceParents; + public System.UInt16[] InstanceFlags; + public System.Int32[] InstanceMeshes; + public System.Int32[] MeshSubmeshOffsets; + public System.Int32[] SubmeshIndexOffsets; + public System.Int32[] SubmeshMaterials; + public Vim.Math3d.Vector4[] MaterialColors; + public System.Single[] MaterialGlossiness; + public System.Single[] MaterialSmoothness; + public Vim.Math3d.Vector3[] ShapeVertices; + public System.Int32[] ShapeVertexOffsets; + public Vim.Math3d.Vector4[] ShapeColors; + public System.Single[] ShapeWidths; + + public G3dVim( + System.Int32[] indices, + Vim.Math3d.Vector3[] positions, + Vim.Math3d.Matrix4x4[] instanceTransforms, + System.Int32[] instanceParents, + System.UInt16[] instanceFlags, + System.Int32[] instanceMeshes, + System.Int32[] meshSubmeshOffsets, + System.Int32[] submeshIndexOffsets, + System.Int32[] submeshMaterials, + Vim.Math3d.Vector4[] materialColors, + System.Single[] materialGlossiness, + System.Single[] materialSmoothness, + Vim.Math3d.Vector3[] shapeVertices, + System.Int32[] shapeVertexOffsets, + Vim.Math3d.Vector4[] shapeColors, + System.Single[] shapeWidths + ) + { + Indices = indices; + Positions = positions; + InstanceTransforms = instanceTransforms; + InstanceParents = instanceParents; + InstanceFlags = instanceFlags; + InstanceMeshes = instanceMeshes; + MeshSubmeshOffsets = meshSubmeshOffsets; + SubmeshIndexOffsets = submeshIndexOffsets; + SubmeshMaterials = submeshMaterials; + MaterialColors = materialColors; + MaterialGlossiness = materialGlossiness; + MaterialSmoothness = materialSmoothness; + ShapeVertices = shapeVertices; + ShapeVertexOffsets = shapeVertexOffsets; + ShapeColors = shapeColors; + ShapeWidths = shapeWidths; + + (this as ISetup).Setup(); + } + + public G3dVim(BFast bfast) + { + Indices = bfast.GetArray("g3d:corner:index:0:int32:1"); + Positions = bfast.GetArray("g3d:vertex:position:0:float32:3"); + InstanceTransforms = bfast.GetArray("g3d:instance:transform:0:float32:16"); + InstanceParents = bfast.GetArray("g3d:instance:parent:0:int32:1"); + InstanceFlags = bfast.GetArray("g3d:instance:flags:0:uint16:1"); + InstanceMeshes = bfast.GetArray("g3d:instance:mesh:0:int32:1"); + MeshSubmeshOffsets = bfast.GetArray("g3d:mesh:submeshoffset:0:int32:1"); + SubmeshIndexOffsets = bfast.GetArray("g3d:submesh:indexoffset:0:int32:1"); + SubmeshMaterials = bfast.GetArray("g3d:submesh:material:0:int32:1"); + MaterialColors = bfast.GetArray("g3d:material:color:0:float32:4"); + MaterialGlossiness = bfast.GetArray("g3d:material:glossiness:0:float32:1"); + MaterialSmoothness = bfast.GetArray("g3d:material:smoothness:0:float32:1"); + ShapeVertices = bfast.GetArray("g3d:shapevertex:position:0:float32:3"); + ShapeVertexOffsets = bfast.GetArray("g3d:shape:vertexoffset:0:int32:1"); + ShapeColors = bfast.GetArray("g3d:shape:color:0:float32:4"); + ShapeWidths = bfast.GetArray("g3d:shape:width:0:float32:1"); + + (this as ISetup).Setup(); + } + + public BFast ToBFast() + { + var bfast = new BFast(); + + bfast.SetArray("g3d:corner:index:0:int32:1", Indices); + bfast.SetArray("g3d:vertex:position:0:float32:3", Positions); + bfast.SetArray("g3d:instance:transform:0:float32:16", InstanceTransforms); + bfast.SetArray("g3d:instance:parent:0:int32:1", InstanceParents); + bfast.SetArray("g3d:instance:flags:0:uint16:1", InstanceFlags); + bfast.SetArray("g3d:instance:mesh:0:int32:1", InstanceMeshes); + bfast.SetArray("g3d:mesh:submeshoffset:0:int32:1", MeshSubmeshOffsets); + bfast.SetArray("g3d:submesh:indexoffset:0:int32:1", SubmeshIndexOffsets); + bfast.SetArray("g3d:submesh:material:0:int32:1", SubmeshMaterials); + bfast.SetArray("g3d:material:color:0:float32:4", MaterialColors); + bfast.SetArray("g3d:material:glossiness:0:float32:1", MaterialGlossiness); + bfast.SetArray("g3d:material:smoothness:0:float32:1", MaterialSmoothness); + bfast.SetArray("g3d:shapevertex:position:0:float32:3", ShapeVertices); + bfast.SetArray("g3d:shape:vertexoffset:0:int32:1", ShapeVertexOffsets); + bfast.SetArray("g3d:shape:color:0:float32:4", ShapeColors); + bfast.SetArray("g3d:shape:width:0:float32:1", ShapeWidths); + + return bfast; + } + + public G3dVim Clone() + { + return this.MemberwiseClone() as G3dVim; + } + + public bool Equals(G3dVim other ) + { + return BufferMethods.SafeEquals(Indices, other.Indices) && + BufferMethods.SafeEquals(Positions, other.Positions) && + BufferMethods.SafeEquals(InstanceTransforms, other.InstanceTransforms) && + BufferMethods.SafeEquals(InstanceParents, other.InstanceParents) && + BufferMethods.SafeEquals(InstanceFlags, other.InstanceFlags) && + BufferMethods.SafeEquals(InstanceMeshes, other.InstanceMeshes) && + BufferMethods.SafeEquals(MeshSubmeshOffsets, other.MeshSubmeshOffsets) && + BufferMethods.SafeEquals(SubmeshIndexOffsets, other.SubmeshIndexOffsets) && + BufferMethods.SafeEquals(SubmeshMaterials, other.SubmeshMaterials) && + BufferMethods.SafeEquals(MaterialColors, other.MaterialColors) && + BufferMethods.SafeEquals(MaterialGlossiness, other.MaterialGlossiness) && + BufferMethods.SafeEquals(MaterialSmoothness, other.MaterialSmoothness) && + BufferMethods.SafeEquals(ShapeVertices, other.ShapeVertices) && + BufferMethods.SafeEquals(ShapeVertexOffsets, other.ShapeVertexOffsets) && + BufferMethods.SafeEquals(ShapeColors, other.ShapeColors) && + BufferMethods.SafeEquals(ShapeWidths, other.ShapeWidths); + } + + public G3dVim Merge(G3dVim other) + { + return new G3dVim( + BufferMethods.MergeIndex(Indices, other.Indices, Positions?.Length ?? 0), + BufferMethods.MergeData(Positions, other.Positions), + BufferMethods.MergeData(InstanceTransforms, other.InstanceTransforms), + BufferMethods.MergeIndex(InstanceParents, other.InstanceParents, InstanceTransforms?.Length ?? 0), + BufferMethods.MergeData(InstanceFlags, other.InstanceFlags), + BufferMethods.MergeIndex(InstanceMeshes, other.InstanceMeshes, MeshSubmeshOffsets?.Length ?? 0), + BufferMethods.MergeIndex(MeshSubmeshOffsets, other.MeshSubmeshOffsets, SubmeshIndexOffsets?.Length ?? 0), + BufferMethods.MergeIndex(SubmeshIndexOffsets, other.SubmeshIndexOffsets, Indices?.Length ?? 0), + BufferMethods.MergeIndex(SubmeshMaterials, other.SubmeshMaterials, MaterialColors?.Length ?? 0), + BufferMethods.MergeData(MaterialColors, other.MaterialColors), + BufferMethods.MergeData(MaterialGlossiness, other.MaterialGlossiness), + BufferMethods.MergeData(MaterialSmoothness, other.MaterialSmoothness), + BufferMethods.MergeData(ShapeVertices, other.ShapeVertices), + BufferMethods.MergeIndex(ShapeVertexOffsets, other.ShapeVertexOffsets, ShapeVertices?.Length ?? 0), + BufferMethods.MergeData(ShapeColors, other.ShapeColors), + BufferMethods.MergeData(ShapeWidths, other.ShapeWidths) + ); + } + + public int CountOf(string name) + { + if(name == "g3d:corner:index:0:int32:1") return Indices?.Length ?? -1; + if(name == "g3d:vertex:position:0:float32:3") return Positions?.Length ?? -1; + if(name == "g3d:instance:transform:0:float32:16") return InstanceTransforms?.Length ?? -1; + if(name == "g3d:instance:parent:0:int32:1") return InstanceParents?.Length ?? -1; + if(name == "g3d:instance:flags:0:uint16:1") return InstanceFlags?.Length ?? -1; + if(name == "g3d:instance:mesh:0:int32:1") return InstanceMeshes?.Length ?? -1; + if(name == "g3d:mesh:submeshoffset:0:int32:1") return MeshSubmeshOffsets?.Length ?? -1; + if(name == "g3d:submesh:indexoffset:0:int32:1") return SubmeshIndexOffsets?.Length ?? -1; + if(name == "g3d:submesh:material:0:int32:1") return SubmeshMaterials?.Length ?? -1; + if(name == "g3d:material:color:0:float32:4") return MaterialColors?.Length ?? -1; + if(name == "g3d:material:glossiness:0:float32:1") return MaterialGlossiness?.Length ?? -1; + if(name == "g3d:material:smoothness:0:float32:1") return MaterialSmoothness?.Length ?? -1; + if(name == "g3d:shapevertex:position:0:float32:3") return ShapeVertices?.Length ?? -1; + if(name == "g3d:shape:vertexoffset:0:int32:1") return ShapeVertexOffsets?.Length ?? -1; + if(name == "g3d:shape:color:0:float32:4") return ShapeColors?.Length ?? -1; + if(name == "g3d:shape:width:0:float32:1") return ShapeWidths?.Length ?? -1; + return -1; + } + + public void Validate() + { + // Ensure all the indices are either -1 or within the bounds of the attributes they are indexing into. + BufferMethods.ValidateIndex(Indices, Positions, "Indices"); + BufferMethods.ValidateIndex(InstanceParents, InstanceTransforms, "InstanceParents"); + BufferMethods.ValidateIndex(InstanceMeshes, MeshSubmeshOffsets, "InstanceMeshes"); + BufferMethods.ValidateIndex(MeshSubmeshOffsets, SubmeshIndexOffsets, "MeshSubmeshOffsets"); + BufferMethods.ValidateIndex(SubmeshIndexOffsets, Indices, "SubmeshIndexOffsets"); + BufferMethods.ValidateIndex(SubmeshMaterials, MaterialColors, "SubmeshMaterials"); + BufferMethods.ValidateIndex(ShapeVertexOffsets, ShapeVertices, "ShapeVertexOffsets"); + } + } + + // Please provide an explicit implementation in another partial class file. + public partial class G3dChunk : ISetup + { + public System.Int32[] MeshOpaqueSubmeshCounts; + public System.Int32[] MeshSubmeshOffset; + public System.Int32[] SubmeshIndexOffsets; + public System.Int32[] SubmeshVertexOffsets; + public System.Int32[] SubmeshMaterials; + public Vim.Math3d.Vector3[] Positions; + public System.Int32[] Indices; + + public G3dChunk( + System.Int32[] meshOpaqueSubmeshCounts, + System.Int32[] meshSubmeshOffset, + System.Int32[] submeshIndexOffsets, + System.Int32[] submeshVertexOffsets, + System.Int32[] submeshMaterials, + Vim.Math3d.Vector3[] positions, + System.Int32[] indices + ) + { + MeshOpaqueSubmeshCounts = meshOpaqueSubmeshCounts; + MeshSubmeshOffset = meshSubmeshOffset; + SubmeshIndexOffsets = submeshIndexOffsets; + SubmeshVertexOffsets = submeshVertexOffsets; + SubmeshMaterials = submeshMaterials; + Positions = positions; + Indices = indices; + + (this as ISetup).Setup(); + } + + public G3dChunk(BFast bfast) + { + MeshOpaqueSubmeshCounts = bfast.GetArray("g3d:mesh:opaquesubmeshcount:0:int32:1"); + MeshSubmeshOffset = bfast.GetArray("g3d:mesh:submeshoffset:0:int32:1"); + SubmeshIndexOffsets = bfast.GetArray("g3d:submesh:indexoffset:0:int32:1"); + SubmeshVertexOffsets = bfast.GetArray("g3d:submesh:vertexoffset:0:int32:1"); + SubmeshMaterials = bfast.GetArray("g3d:submesh:material:0:int32:1"); + Positions = bfast.GetArray("g3d:vertex:position:0:float32:3"); + Indices = bfast.GetArray("g3d:corner:index:0:int32:1"); + + (this as ISetup).Setup(); + } + + public BFast ToBFast() + { + var bfast = new BFast(); + + bfast.SetArray("g3d:mesh:opaquesubmeshcount:0:int32:1", MeshOpaqueSubmeshCounts); + bfast.SetArray("g3d:mesh:submeshoffset:0:int32:1", MeshSubmeshOffset); + bfast.SetArray("g3d:submesh:indexoffset:0:int32:1", SubmeshIndexOffsets); + bfast.SetArray("g3d:submesh:vertexoffset:0:int32:1", SubmeshVertexOffsets); + bfast.SetArray("g3d:submesh:material:0:int32:1", SubmeshMaterials); + bfast.SetArray("g3d:vertex:position:0:float32:3", Positions); + bfast.SetArray("g3d:corner:index:0:int32:1", Indices); + + return bfast; + } + + public G3dChunk Clone() + { + return this.MemberwiseClone() as G3dChunk; + } + + public bool Equals(G3dChunk other ) + { + return BufferMethods.SafeEquals(MeshOpaqueSubmeshCounts, other.MeshOpaqueSubmeshCounts) && + BufferMethods.SafeEquals(MeshSubmeshOffset, other.MeshSubmeshOffset) && + BufferMethods.SafeEquals(SubmeshIndexOffsets, other.SubmeshIndexOffsets) && + BufferMethods.SafeEquals(SubmeshVertexOffsets, other.SubmeshVertexOffsets) && + BufferMethods.SafeEquals(SubmeshMaterials, other.SubmeshMaterials) && + BufferMethods.SafeEquals(Positions, other.Positions) && + BufferMethods.SafeEquals(Indices, other.Indices); + } + + public G3dChunk Merge(G3dChunk other) + { + return new G3dChunk( + BufferMethods.MergeData(MeshOpaqueSubmeshCounts, other.MeshOpaqueSubmeshCounts), + BufferMethods.MergeIndex(MeshSubmeshOffset, other.MeshSubmeshOffset, Indices?.Length ?? 0), + BufferMethods.MergeIndex(SubmeshIndexOffsets, other.SubmeshIndexOffsets, Indices?.Length ?? 0), + BufferMethods.MergeIndex(SubmeshVertexOffsets, other.SubmeshVertexOffsets, Indices?.Length ?? 0), + BufferMethods.MergeData(SubmeshMaterials, other.SubmeshMaterials), + BufferMethods.MergeData(Positions, other.Positions), + BufferMethods.MergeIndex(Indices, other.Indices, Positions?.Length ?? 0) + ); + } + + public int CountOf(string name) + { + if(name == "g3d:mesh:opaquesubmeshcount:0:int32:1") return MeshOpaqueSubmeshCounts?.Length ?? -1; + if(name == "g3d:mesh:submeshoffset:0:int32:1") return MeshSubmeshOffset?.Length ?? -1; + if(name == "g3d:submesh:indexoffset:0:int32:1") return SubmeshIndexOffsets?.Length ?? -1; + if(name == "g3d:submesh:vertexoffset:0:int32:1") return SubmeshVertexOffsets?.Length ?? -1; + if(name == "g3d:submesh:material:0:int32:1") return SubmeshMaterials?.Length ?? -1; + if(name == "g3d:vertex:position:0:float32:3") return Positions?.Length ?? -1; + if(name == "g3d:corner:index:0:int32:1") return Indices?.Length ?? -1; + return -1; + } + + public void Validate() + { + // Ensure all the indices are either -1 or within the bounds of the attributes they are indexing into. + BufferMethods.ValidateIndex(MeshSubmeshOffset, Indices, "MeshSubmeshOffset"); + BufferMethods.ValidateIndex(SubmeshIndexOffsets, Indices, "SubmeshIndexOffsets"); + BufferMethods.ValidateIndex(SubmeshVertexOffsets, Indices, "SubmeshVertexOffsets"); + BufferMethods.ValidateIndex(Indices, Positions, "Indices"); + } + } + + // Please provide an explicit implementation in another partial class file. + public partial class G3dMaterials : ISetup + { + public Vim.Math3d.Vector4[] MaterialColors; + public System.Single[] MaterialGlossiness; + public System.Single[] MaterialSmoothness; + + public G3dMaterials( + Vim.Math3d.Vector4[] materialColors, + System.Single[] materialGlossiness, + System.Single[] materialSmoothness + ) + { + MaterialColors = materialColors; + MaterialGlossiness = materialGlossiness; + MaterialSmoothness = materialSmoothness; + + (this as ISetup).Setup(); + } + + public G3dMaterials(BFast bfast) + { + MaterialColors = bfast.GetArray("g3d:material:color:0:float32:4"); + MaterialGlossiness = bfast.GetArray("g3d:material:glossiness:0:float32:1"); + MaterialSmoothness = bfast.GetArray("g3d:material:smoothness:0:float32:1"); + + (this as ISetup).Setup(); + } + + public BFast ToBFast() + { + var bfast = new BFast(); + + bfast.SetArray("g3d:material:color:0:float32:4", MaterialColors); + bfast.SetArray("g3d:material:glossiness:0:float32:1", MaterialGlossiness); + bfast.SetArray("g3d:material:smoothness:0:float32:1", MaterialSmoothness); + + return bfast; + } + + public G3dMaterials Clone() + { + return this.MemberwiseClone() as G3dMaterials; + } + + public bool Equals(G3dMaterials other ) + { + return BufferMethods.SafeEquals(MaterialColors, other.MaterialColors) && + BufferMethods.SafeEquals(MaterialGlossiness, other.MaterialGlossiness) && + BufferMethods.SafeEquals(MaterialSmoothness, other.MaterialSmoothness); + } + + public G3dMaterials Merge(G3dMaterials other) + { + return new G3dMaterials( + BufferMethods.MergeData(MaterialColors, other.MaterialColors), + BufferMethods.MergeData(MaterialGlossiness, other.MaterialGlossiness), + BufferMethods.MergeData(MaterialSmoothness, other.MaterialSmoothness) + ); + } + + public int CountOf(string name) + { + if(name == "g3d:material:color:0:float32:4") return MaterialColors?.Length ?? -1; + if(name == "g3d:material:glossiness:0:float32:1") return MaterialGlossiness?.Length ?? -1; + if(name == "g3d:material:smoothness:0:float32:1") return MaterialSmoothness?.Length ?? -1; + return -1; + } + + public void Validate() + { + // Ensure all the indices are either -1 or within the bounds of the attributes they are indexing into. + + } + } + + // Please provide an explicit implementation in another partial class file. + public partial class G3dScene : ISetup + { + public System.Int32[] ChunkCount; + public System.Int32[] InstanceMeshes; + public Vim.Math3d.Matrix4x4[] InstanceTransformData; + public System.Int32[] InstanceNodes; + public System.Int32[] InstanceGroups; + public System.Int64[] InstanceTags; + public System.UInt16[] InstanceFlags; + public Vim.Math3d.Vector3[] InstanceMins; + public Vim.Math3d.Vector3[] InstanceMaxs; + public System.Int32[] MeshChunks; + public System.Int32[] MeshChunkIndices; + public System.Int32[] MeshVertexCounts; + public System.Int32[] MeshIndexCounts; + public System.Int32[] MeshOpaqueVertexCounts; + public System.Int32[] MeshOpaqueIndexCounts; + + public G3dScene( + System.Int32[] chunkCount, + System.Int32[] instanceMeshes, + Vim.Math3d.Matrix4x4[] instanceTransformData, + System.Int32[] instanceNodes, + System.Int32[] instanceGroups, + System.Int64[] instanceTags, + System.UInt16[] instanceFlags, + Vim.Math3d.Vector3[] instanceMins, + Vim.Math3d.Vector3[] instanceMaxs, + System.Int32[] meshChunks, + System.Int32[] meshChunkIndices, + System.Int32[] meshVertexCounts, + System.Int32[] meshIndexCounts, + System.Int32[] meshOpaqueVertexCounts, + System.Int32[] meshOpaqueIndexCounts + ) + { + ChunkCount = chunkCount; + InstanceMeshes = instanceMeshes; + InstanceTransformData = instanceTransformData; + InstanceNodes = instanceNodes; + InstanceGroups = instanceGroups; + InstanceTags = instanceTags; + InstanceFlags = instanceFlags; + InstanceMins = instanceMins; + InstanceMaxs = instanceMaxs; + MeshChunks = meshChunks; + MeshChunkIndices = meshChunkIndices; + MeshVertexCounts = meshVertexCounts; + MeshIndexCounts = meshIndexCounts; + MeshOpaqueVertexCounts = meshOpaqueVertexCounts; + MeshOpaqueIndexCounts = meshOpaqueIndexCounts; + + (this as ISetup).Setup(); + } + + public G3dScene(BFast bfast) + { + ChunkCount = bfast.GetArray("g3d:chunk:count:0:int32:1"); + InstanceMeshes = bfast.GetArray("g3d:instance:mesh:0:int32:1"); + InstanceTransformData = bfast.GetArray("g3d:instance:transform:0:float32:16"); + InstanceNodes = bfast.GetArray("g3d:instance:node:0:int32:1"); + InstanceGroups = bfast.GetArray("g3d:instance:group:0:int32:1"); + InstanceTags = bfast.GetArray("g3d:instance:tag:0:int64:1"); + InstanceFlags = bfast.GetArray("g3d:instance:flags:0:uint16:1"); + InstanceMins = bfast.GetArray("g3d:instance:min:0:float32:3"); + InstanceMaxs = bfast.GetArray("g3d:instance:max:0:float32:3"); + MeshChunks = bfast.GetArray("g3d:mesh:chunk:0:int32:1"); + MeshChunkIndices = bfast.GetArray("g3d:mesh:chunkindex:0:int32:1"); + MeshVertexCounts = bfast.GetArray("g3d:mesh:vertexcount:0:int32:1"); + MeshIndexCounts = bfast.GetArray("g3d:mesh:indexcount:0:int32:1"); + MeshOpaqueVertexCounts = bfast.GetArray("g3d:mesh:opaquevertexcount:0:int32:1"); + MeshOpaqueIndexCounts = bfast.GetArray("g3d:mesh:opaqueindexcount:0:int32:1"); + + (this as ISetup).Setup(); + } + + public BFast ToBFast() + { + var bfast = new BFast(); + + bfast.SetArray("g3d:chunk:count:0:int32:1", ChunkCount); + bfast.SetArray("g3d:instance:mesh:0:int32:1", InstanceMeshes); + bfast.SetArray("g3d:instance:transform:0:float32:16", InstanceTransformData); + bfast.SetArray("g3d:instance:node:0:int32:1", InstanceNodes); + bfast.SetArray("g3d:instance:group:0:int32:1", InstanceGroups); + bfast.SetArray("g3d:instance:tag:0:int64:1", InstanceTags); + bfast.SetArray("g3d:instance:flags:0:uint16:1", InstanceFlags); + bfast.SetArray("g3d:instance:min:0:float32:3", InstanceMins); + bfast.SetArray("g3d:instance:max:0:float32:3", InstanceMaxs); + bfast.SetArray("g3d:mesh:chunk:0:int32:1", MeshChunks); + bfast.SetArray("g3d:mesh:chunkindex:0:int32:1", MeshChunkIndices); + bfast.SetArray("g3d:mesh:vertexcount:0:int32:1", MeshVertexCounts); + bfast.SetArray("g3d:mesh:indexcount:0:int32:1", MeshIndexCounts); + bfast.SetArray("g3d:mesh:opaquevertexcount:0:int32:1", MeshOpaqueVertexCounts); + bfast.SetArray("g3d:mesh:opaqueindexcount:0:int32:1", MeshOpaqueIndexCounts); + + return bfast; + } + + public G3dScene Clone() + { + return this.MemberwiseClone() as G3dScene; + } + + public bool Equals(G3dScene other ) + { + return BufferMethods.SafeEquals(ChunkCount, other.ChunkCount) && + BufferMethods.SafeEquals(InstanceMeshes, other.InstanceMeshes) && + BufferMethods.SafeEquals(InstanceTransformData, other.InstanceTransformData) && + BufferMethods.SafeEquals(InstanceNodes, other.InstanceNodes) && + BufferMethods.SafeEquals(InstanceGroups, other.InstanceGroups) && + BufferMethods.SafeEquals(InstanceTags, other.InstanceTags) && + BufferMethods.SafeEquals(InstanceFlags, other.InstanceFlags) && + BufferMethods.SafeEquals(InstanceMins, other.InstanceMins) && + BufferMethods.SafeEquals(InstanceMaxs, other.InstanceMaxs) && + BufferMethods.SafeEquals(MeshChunks, other.MeshChunks) && + BufferMethods.SafeEquals(MeshChunkIndices, other.MeshChunkIndices) && + BufferMethods.SafeEquals(MeshVertexCounts, other.MeshVertexCounts) && + BufferMethods.SafeEquals(MeshIndexCounts, other.MeshIndexCounts) && + BufferMethods.SafeEquals(MeshOpaqueVertexCounts, other.MeshOpaqueVertexCounts) && + BufferMethods.SafeEquals(MeshOpaqueIndexCounts, other.MeshOpaqueIndexCounts); + } + + public G3dScene Merge(G3dScene other) + { + return new G3dScene( + BufferMethods.MergeData(ChunkCount, other.ChunkCount), + BufferMethods.MergeData(InstanceMeshes, other.InstanceMeshes), + BufferMethods.MergeData(InstanceTransformData, other.InstanceTransformData), + BufferMethods.MergeData(InstanceNodes, other.InstanceNodes), + BufferMethods.MergeData(InstanceGroups, other.InstanceGroups), + BufferMethods.MergeData(InstanceTags, other.InstanceTags), + BufferMethods.MergeData(InstanceFlags, other.InstanceFlags), + BufferMethods.MergeData(InstanceMins, other.InstanceMins), + BufferMethods.MergeData(InstanceMaxs, other.InstanceMaxs), + BufferMethods.MergeData(MeshChunks, other.MeshChunks), + BufferMethods.MergeData(MeshChunkIndices, other.MeshChunkIndices), + BufferMethods.MergeData(MeshVertexCounts, other.MeshVertexCounts), + BufferMethods.MergeData(MeshIndexCounts, other.MeshIndexCounts), + BufferMethods.MergeData(MeshOpaqueVertexCounts, other.MeshOpaqueVertexCounts), + BufferMethods.MergeData(MeshOpaqueIndexCounts, other.MeshOpaqueIndexCounts) + ); + } + + public int CountOf(string name) + { + if(name == "g3d:chunk:count:0:int32:1") return ChunkCount?.Length ?? -1; + if(name == "g3d:instance:mesh:0:int32:1") return InstanceMeshes?.Length ?? -1; + if(name == "g3d:instance:transform:0:float32:16") return InstanceTransformData?.Length ?? -1; + if(name == "g3d:instance:node:0:int32:1") return InstanceNodes?.Length ?? -1; + if(name == "g3d:instance:group:0:int32:1") return InstanceGroups?.Length ?? -1; + if(name == "g3d:instance:tag:0:int64:1") return InstanceTags?.Length ?? -1; + if(name == "g3d:instance:flags:0:uint16:1") return InstanceFlags?.Length ?? -1; + if(name == "g3d:instance:min:0:float32:3") return InstanceMins?.Length ?? -1; + if(name == "g3d:instance:max:0:float32:3") return InstanceMaxs?.Length ?? -1; + if(name == "g3d:mesh:chunk:0:int32:1") return MeshChunks?.Length ?? -1; + if(name == "g3d:mesh:chunkindex:0:int32:1") return MeshChunkIndices?.Length ?? -1; + if(name == "g3d:mesh:vertexcount:0:int32:1") return MeshVertexCounts?.Length ?? -1; + if(name == "g3d:mesh:indexcount:0:int32:1") return MeshIndexCounts?.Length ?? -1; + if(name == "g3d:mesh:opaquevertexcount:0:int32:1") return MeshOpaqueVertexCounts?.Length ?? -1; + if(name == "g3d:mesh:opaqueindexcount:0:int32:1") return MeshOpaqueIndexCounts?.Length ?? -1; + return -1; + } + + public void Validate() + { + // Ensure all the indices are either -1 or within the bounds of the attributes they are indexing into. + + } + } + +} diff --git a/src/cs/g3d/Vim.G3d/G3dMaterial.cs b/src/cs/g3d/Vim.G3d/G3dMaterial.cs deleted file mode 100644 index 9dc3b5bf..00000000 --- a/src/cs/g3d/Vim.G3d/G3dMaterial.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Vim.Math3d; - -namespace Vim.G3d -{ - public class G3dMaterial - { - public readonly G3D G3d; - public readonly int Index; - - public G3dMaterial(G3D g3D, int index) - { - G3d = g3D; - Index = index; - } - - public Vector4 Color => G3d.MaterialColors[Index]; - public float Glossiness => G3d?.MaterialGlossiness[Index] ?? 0f; - public float Smoothness => G3d?.MaterialSmoothness[Index] ?? 0f; - } -} diff --git a/src/cs/g3d/Vim.G3d/G3dMaterials.cs b/src/cs/g3d/Vim.G3d/G3dMaterials.cs new file mode 100644 index 00000000..28888a7e --- /dev/null +++ b/src/cs/g3d/Vim.G3d/G3dMaterials.cs @@ -0,0 +1,18 @@ + +namespace Vim.G3d +{ + public partial class G3dMaterials + { + void ISetup.Setup() + { + // empty + } + + public G3dMaterials(G3dVim vim) + { + MaterialColors = vim.MaterialColors; + MaterialGlossiness = vim.MaterialGlossiness; + MaterialSmoothness = vim.MaterialSmoothness; + } + } +} diff --git a/src/cs/g3d/Vim.G3d/G3dMesh.cs b/src/cs/g3d/Vim.G3d/G3dMesh.cs deleted file mode 100644 index 689122c5..00000000 --- a/src/cs/g3d/Vim.G3d/G3dMesh.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.G3d -{ - /// - /// A G3dMesh is a section of the G3D data that defines a mesh. - /// This does not implement IGeometryAttributes for performance reasons. - /// - public class G3dMesh - { - public G3D G3D { get; } - public int Index { get; } - - public int VertexOffset => G3D.MeshVertexOffsets[Index]; - public int NumVertices => G3D.MeshVertexCounts[Index]; - public int IndexOffset => G3D.MeshIndexOffsets[Index]; - public int NumCorners => G3D.MeshIndexCounts[Index]; - public int FaceOffset => IndexOffset / NumCornersPerFace; - public int NumFaces => NumCorners / NumCornersPerFace; - public int NumCornersPerFace => G3D.NumCornersPerFace; - - public G3dMesh(G3D parent, int index) - { - (G3D, Index) = (parent, index); - Vertices = G3D.Vertices?.SubArray(VertexOffset, NumVertices); - var offset = VertexOffset; - Indices = G3D.Indices?.SubArray(IndexOffset, NumCorners).Select(i => i - offset); - VertexUvs = G3D.VertexUvs?.SubArray(VertexOffset, NumVertices); - VertexNormals = G3D.VertexNormals?.SubArray(VertexOffset, NumVertices); - VertexColors = G3D.VertexColors?.SubArray(VertexOffset, NumVertices); - VertexTangents = G3D.VertexTangents?.SubArray(VertexOffset, NumVertices); - FaceNormals = G3D.FaceNormals?.SubArray(FaceOffset, NumFaces); - - // TODO: Remove need for this. - var submeshArray = (G3D.SubmeshIndexOffsets as ArrayAdapter).Array; - var submeshIndex = Array.BinarySearch(submeshArray, IndexOffset); - var submeshCount = 0; - for(var i = submeshIndex; i < submeshArray.Length; i++) - { - var indexOffset = submeshArray[i]; - if (indexOffset - IndexOffset >= NumCorners) - break; - submeshCount++; - } - SubmeshMaterials = G3D.SubmeshMaterials?.SubArray(submeshIndex, submeshCount); - SubmeshIndexOffsets = G3D.SubmeshIndexOffsets?.SubArray(submeshIndex, submeshCount).Select(i => i-IndexOffset); - MeshSubmeshOffset = new List() {0}.ToIArray(); - } - - // Vertex buffer. Usually present. - public IArray Vertices { get; } - - // Index buffer (one index per corner, and per half-edge) - public IArray Indices { get; } - - // Vertex associated data - public IArray VertexUvs { get; } - public IArray VertexNormals { get; } - public IArray VertexColors { get; } - public IArray VertexTangents { get; } - - // Face associated data. - public IArray FaceNormals { get; } - - public IArray SubmeshMaterials { get; } - public IArray SubmeshIndexOffsets { get; } - public IArray MeshSubmeshOffset { get; } - } -} diff --git a/src/cs/g3d/Vim.G3d/G3dScene.cs b/src/cs/g3d/Vim.G3d/G3dScene.cs new file mode 100644 index 00000000..1fe8d9e2 --- /dev/null +++ b/src/cs/g3d/Vim.G3d/G3dScene.cs @@ -0,0 +1,12 @@ +namespace Vim.G3d +{ + public partial class G3dScene + { + public int GetChunksCount() => ChunkCount[0]; + public int GetInstanceCount() => InstanceMeshes.Length; + void ISetup.Setup() + { + // empty + } + } +} diff --git a/src/cs/g3d/Vim.G3d/G3dSerialization.cs b/src/cs/g3d/Vim.G3d/G3dSerialization.cs deleted file mode 100644 index ba7e59a7..00000000 --- a/src/cs/g3d/Vim.G3d/G3dSerialization.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using Vim.BFast; - -namespace Vim.G3d -{ - public static partial class G3DExtension - { - public static void WriteAttribute(Stream stream, GeometryAttribute attribute, string name, long size) - { - var buffer = attribute.ToBuffer(); - if (buffer.NumBytes() != size) - throw new Exception($"Internal error while writing attribute, expected number of bytes was {size} but instead was {buffer.NumBytes()}"); - if (buffer.Name != name) - throw new Exception($"Internal error while writing attribute, expected name was {name} but instead was {buffer.Name}"); - stream.Write(buffer); - } - - public static G3dWriter ToG3DWriter(this IGeometryAttributes self, G3dHeader? header = null) - => new G3dWriter(self, header); - - public static void Write(this IGeometryAttributes self, Stream stream, G3dHeader? header = null) - => self.ToG3DWriter(header).Write(stream); - - public static void Write(this IGeometryAttributes self, string filePath, G3dHeader? header = null) - { - using (var stream = File.OpenWrite(filePath)) - self.Write(stream, header); - } - - public static byte[] WriteToBytes(this IGeometryAttributes self) - { - using (var memoryStream = new MemoryStream()) - { - self.Write(memoryStream); - return memoryStream.ToArray(); - } - } - - public static bool TryReadHeader(Stream stream, long size, out G3dHeader outHeader) - { - var buffer = stream.ReadArray((int)size); - - if (buffer[0] == G3dHeader.MagicA && buffer[1] == G3dHeader.MagicB) - { - outHeader = G3dHeader.FromBytes(buffer); - return true; - } - else - { - outHeader = default; - return false; - } - } - - public static bool TryReadGeometryAttribute(Stream stream, string name, long size, out GeometryAttribute geometryAttribute) - { - geometryAttribute = null; - - bool ReadFailure() - { - // Update the seek head to consume the stream and return false. - stream.Seek((int)size, SeekOrigin.Current); - return false; - } - - if (!AttributeDescriptor.TryParse(name, out var attributeDescriptor)) - { - // Skip unknown attribute descriptors. - return ReadFailure(); - } - - // Populate a default attribute with the parsed attribute descriptor. - GeometryAttribute defaultAttribute; - try - { - defaultAttribute = attributeDescriptor.ToDefaultAttribute(0); - } - catch - { - // Eat the exception and return. - return ReadFailure(); - } - - // Success; consume the stream. - geometryAttribute = defaultAttribute.Read(stream, size); - return true; - } - - public static G3D ReadG3d(this Stream stream, Func renameFunc = null) - { - var header = G3dHeader.Default; - - GeometryAttribute ReadG3dSegment(Stream s2, string name, long size) - { - name = renameFunc?.Invoke(name) ?? name; - - // Check for the G3dHeader - if (name == "meta" && size == 8) - { - if (TryReadHeader(s2, size, out var outHeader)) - { - // Assign to the header variable in the closure. - header = outHeader; - } - - return null; - } - else - { - return TryReadGeometryAttribute(s2, name, size, out var geometryAttribute) - ? geometryAttribute - : null; - } - - } - - var results = stream.ReadBFast(ReadG3dSegment).Select(r => r.Item2); - return new G3D(results.Where(x => x != null), header); - } - } -} diff --git a/src/cs/g3d/Vim.G3d/G3dShape.cs b/src/cs/g3d/Vim.G3d/G3dShape.cs deleted file mode 100644 index 9cd220ad..00000000 --- a/src/cs/g3d/Vim.G3d/G3dShape.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.G3d -{ - public class G3dShape - { - public readonly G3D G3D; - public readonly int Index; - public readonly IArray Vertices; - - public int ShapeVertexOffset => G3D.ShapeVertexOffsets[Index]; - public int NumVertices => G3D.ShapeVertexCounts[Index]; - public Vector4 Color => G3D.ShapeColors[Index]; - public float Width => G3D.ShapeWidths[Index]; - - public G3dShape(G3D parent, int index) - { - (G3D, Index) = (parent, index); - Vertices = G3D.ShapeVertices?.SubArray(ShapeVertexOffset, NumVertices); - } - } -} diff --git a/src/cs/g3d/Vim.G3d/G3dVim.cs b/src/cs/g3d/Vim.G3d/G3dVim.cs new file mode 100644 index 00000000..ed19c843 --- /dev/null +++ b/src/cs/g3d/Vim.G3d/G3dVim.cs @@ -0,0 +1,246 @@ +using System; +using System.Collections.Generic; +using Vim.BFastLib; + +namespace Vim.G3d +{ + [Flags] + public enum InstanceFlags + { + /// + /// Default - no instance options defined. + /// + None = 0, + + /// + /// When enabled, indicates that the renderer (or the consuming application) should hide + /// the instance by default. + /// + Hidden = 1, + } + + public partial class G3dVim + { + + // Computed field + public int[] MeshVertexOffsets; + public int[] SubmeshVertexOffsets; + private List[] _meshInstances; + + public IReadOnlyList GetMeshInstances(int mesh) + { + return _meshInstances[mesh]; + } + + public int GetApproxSize(int mesh) + { + return GetMeshVertexCount(mesh) * 12 + GetMeshIndexCount(mesh) * 4; + } + + public bool InstanceHasFlag(int index, InstanceFlags flag) + { + return InstanceFlags[index] == (int)flag; + } + + void ISetup.Setup() + { + MeshVertexOffsets = ComputeMeshVertexOffsets(); + SubmeshVertexOffsets = ComputeSubmeshVertexOffsets(); + _meshInstances = ComputeMeshInstances(); + } + + public static G3dVim FromVim(string vimPath) + => BFastHelpers.Read(vimPath, b => new G3dVim(b.GetBFast("geometry"))); + + private int[] ComputeMeshVertexOffsets() + { + var result = new int[GetMeshCount()]; + for (var m = 0; m < result.Length; m++) + { + var min = int.MaxValue; + var start = GetMeshIndexStart(m); + var end = GetMeshIndexEnd(m); + for (var i = start; i < end; i++) + { + min = Math.Min(min, Indices[i]); + } + result[m] = min; + } + return result; + } + + private int[] ComputeSubmeshVertexOffsets() + { + var result = new int[GetSubmeshCount()]; + for (var m = 0; m < result.Length; m++) + { + var min = int.MaxValue; + var start = SubmeshIndexOffsets[m]; + var end = m < SubmeshIndexOffsets.Length ? SubmeshIndexOffsets[m + 1] : SubmeshIndexOffsets.Length; + for (var i = start; i < end; i++) + { + min = Math.Min(min, Indices[i]); + } + result[m] = min; + } + return result; + } + + private List[] ComputeMeshInstances() + { + var result = new List[GetMeshCount()]; + for (var i = 0; i < result.Length; i++) + { + result[i] = new List(); + } + + for (var i = 0; i < InstanceMeshes.Length; i++) + { + var mesh = InstanceMeshes[i]; + if (mesh >= 0) + { + result[mesh].Add(i); + } + } + + return result; + } + + public G3dVim RemoveShapes() + { + var result = Clone(); + result.ShapeVertexOffsets = null; + result.ShapeWidths = null; + result.ShapeColors = null; + result.ShapeVertices = null; + return result; + } + + /// + /// The total number of instances. + /// + public int GetInstanceCount() => InstanceTransforms?.Length ?? 0; + + #region meshes + /// + /// The total number of meshes. + /// + public int GetMeshCount() => MeshSubmeshOffsets?.Length ?? 0; + + public int GetMeshIndexStart(int mesh) + { + var submesh = GetMeshSubmeshStart(mesh); + return GetSubmeshIndexStart(submesh); + } + + public int GetMeshIndexEnd(int mesh) + { + var submesh = GetMeshSubmeshEnd(mesh) - 1; + return GetSubmeshIndexEnd(submesh); + } + + public int GetMeshIndexCount(int mesh) + { + return GetMeshIndexEnd(mesh) - GetMeshIndexStart(mesh); + } + + public int GetMeshVertexStart(int mesh) + { + return MeshVertexOffsets[mesh]; + } + + public int GetMeshVertexEnd(int mesh) + { + return mesh + 1 < GetMeshCount() ? MeshVertexOffsets[mesh + 1] : Positions.Length; + } + + public int GetMeshVertexCount(int mesh) + { + return GetMeshVertexEnd(mesh) - GetMeshVertexStart(mesh); + } + + public int GetMeshSubmeshStart(int mesh) + { + return MeshSubmeshOffsets[mesh]; + } + + public int GetMeshSubmeshEnd(int mesh) + { + return mesh + 1 < GetMeshCount() + ? MeshSubmeshOffsets[mesh + 1] + : GetSubmeshCount(); + } + + public int GetMeshSubmeshCount(int mesh) + { + return GetMeshSubmeshEnd(mesh) - GetMeshSubmeshStart(mesh); + } + + #endregion + + #region submesh + + /// + /// The total number of submeshes. + /// + public int GetSubmeshCount() => SubmeshIndexOffsets?.Length ?? 0; + + public int GetSubmeshIndexStart(int submesh) + { + return SubmeshIndexOffsets[submesh]; + } + + public int GetSubmeshIndexEnd(int submesh) + { + return submesh + 1 < GetSubmeshCount() ? SubmeshIndexOffsets[submesh + 1] : GetIndexCount(); + } + + public int GetSubmeshIndexCount(int submesh) + { + return GetSubmeshIndexEnd(submesh) - GetSubmeshIndexStart(submesh); + } + + #endregion + + /// + /// The total number of indices. + /// + public int GetIndexCount() => Indices?.Length ?? 0; + + /// + /// The total number of vertices. + /// + public int GetVertexCount() => Positions?.Length ?? 0; + + /// + /// The total number of materials. + /// + public int GetMaterialCount() => MaterialColors?.Length ?? 0; + + /// + /// The total number of shapes. + /// + public int GetShapeCount() => ShapeVertexOffsets?.Length ?? 0; + + + public int GetShapeVertexStart(int index) + { + return ShapeVertexOffsets[index]; + } + public int GetShapeVertexEnd(int index) + { + if (index + 1 >= ShapeVertexOffsets.Length) + { + return ShapeVertices.Length; + } + return ShapeVertexOffsets[index + 1]; + } + /// + /// The total number of shape vertices. + /// + public int GetShapeVertexCount(int index) + { + return GetShapeVertexEnd(index) - GetShapeVertexStart(index); + } + } +} diff --git a/src/cs/g3d/Vim.G3d/G3dWriter.cs b/src/cs/g3d/Vim.G3d/G3dWriter.cs deleted file mode 100644 index 0d3a9ca8..00000000 --- a/src/cs/g3d/Vim.G3d/G3dWriter.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.IO; -using System.Linq; -using Vim.BFast; -using Vim.LinqArray; - -namespace Vim.G3d -{ - public static partial class G3DExtension - { - /// - /// This is a helper class for writing G3Ds - /// - public class G3dWriter : IBFastComponent - { - public INamedBuffer Meta { get; } - public string[] Names { get; } - public long[] Sizes { get; } - BFastHeader Header { get; } - IGeometryAttributes Attributes { get; } - - public G3dWriter(IGeometryAttributes g, G3dHeader? header = null) - { - Attributes = g; - Meta = (header ?? G3dHeader.Default).ToBytes().ToNamedBuffer("meta"); - Names = new[] { Meta.Name }.Concat(g.Attributes.ToEnumerable().Select(attr => attr.Name)).ToArray(); - Sizes = new[] { Meta.NumBytes() }.Concat(g.Attributes.ToEnumerable().Select(attr => attr.GetByteSize())).ToArray(); - Header = BFast.BFast.CreateBFastHeader(Sizes, Names); - } - - public long GetSize() - => Header.Preamble.DataEnd; - - public void Write(Stream stream) - { - stream.WriteBFastHeader(Header); - stream.WriteBFastBody(Header, Names, Sizes, (_stream, index, name, size) => - { - if (index == 0) - _stream.Write(Meta); - else - WriteAttribute(_stream, Attributes.Attributes[index - 1], name, size); - return size; - }); - } - } - } -} diff --git a/src/cs/g3d/Vim.G3d/GeometryAttribute.cs b/src/cs/g3d/Vim.G3d/GeometryAttribute.cs deleted file mode 100644 index 221a576e..00000000 --- a/src/cs/g3d/Vim.G3d/GeometryAttribute.cs +++ /dev/null @@ -1,226 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Vim.BFast; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.G3d -{ - /// - /// A mesh attribute is an array of data associated with some component of a mesh. - /// It could be vertices, corners, faces, face groups, sub-meshes, instances or the entire mesh. - /// This is the base class of a typed MeshAttribute. - /// It provides two core operations we are the foundation for mesh manipulation: - /// 1. concatenation with like-typed attributes - /// 2. remapping - /// - public abstract class GeometryAttribute - { - /// - /// The descriptor contains information about the data contained in the attribute: - /// * the primitive data type - /// * the arity - /// * the association - /// * the semantic - /// - public AttributeDescriptor Descriptor { get; } - - /// - /// A "name" is a string encoding of the attribute descriptor. - /// - public string Name - => Descriptor.Name; - - /// - /// This is the number of data elements in the attribute. This is equal to - /// the number of primitives times the arity. All mesh attributes associated - /// with the same mesh component (e.g. vertices) must have the same element count. - /// - public int ElementCount { get; } - - /// - /// Constructor. - /// - protected GeometryAttribute(AttributeDescriptor descriptor, int count) - => (Descriptor, ElementCount) = (descriptor, count); - - /// - /// Multiple mesh attributes can be merged together if they have the same - /// underlying descriptor and data type. - /// - public abstract GeometryAttribute Merge(IEnumerable others); - - /// - /// A mesh attribute can be remapped, using the given indices. - /// - public abstract GeometryAttribute Remap(IArray indices); - - /// - /// Converted to an INamedBuffer which consists of a name and an array of unmanaged types. - /// - public abstract INamedBuffer ToBuffer(); - - /// - /// Convenience function to check if this object is a mesh attribute of the given type. - /// - public bool IsType() where T : unmanaged - => this is GeometryAttribute; - - /// - /// Convenience function to check if this object is a mesh attribute of the given type, and the association matches. - /// - public bool IsTypeAndAssociation(Association assoc) where T : unmanaged - => Descriptor.Association == assoc && this is GeometryAttribute; - - /// - /// Convenience function to cast this object into a mesh attribute of the given type, throwing an exception if not possible, - /// - public GeometryAttribute AsType() where T : unmanaged - => this as GeometryAttribute ?? throw new Exception($"The type of the attribute is {GetType()} not MeshAttribute<{typeof(T)}>"); - - /// - /// Loads the correct typed data from a Stream. - /// - public abstract GeometryAttribute Read(Stream stream, long byteCount); - - /// - /// Creates a new GeometryAttribute with the same data, but with a different index. Useful when constructing attributes - /// - public abstract GeometryAttribute SetIndex(int index); - } - - /// - /// This is a typed attribute associated with some part of the mesh. - /// The underlying data is an IArray which means that it can be - /// computed on demand. - /// - public class GeometryAttribute : GeometryAttribute where T : unmanaged - { - public IArray Data; - - public GeometryAttribute(IArray data, AttributeDescriptor descriptor) - : base(descriptor, data.Count) - { - Data = data; - int arity; - DataType dataType; - // TODO: TECH DEBT - Support unsigned tuples in Math3d - if (typeof(T) == typeof(byte)) - (arity, dataType) = (1, DataType.dt_uint8); - else if (typeof(T) == typeof(sbyte)) - (arity, dataType) = (1, DataType.dt_int8); - else if (typeof(T) == typeof(Byte2)) - (arity, dataType) = (2, DataType.dt_int8); - else if (typeof(T) == typeof(Byte3)) - (arity, dataType) = (3, DataType.dt_int8); - else if (typeof(T) == typeof(Byte4)) - (arity, dataType) = (4, DataType.dt_int8); - else if (typeof(T) == typeof(ushort)) - (arity, dataType) = (1, DataType.dt_uint16); - else if (typeof(T) == typeof(short)) - (arity, dataType) = (1, DataType.dt_int16); - else if (typeof(T) == typeof(uint)) - (arity, dataType) = (1, DataType.dt_uint32); - else if (typeof(T) == typeof(int)) - (arity, dataType) = (1, DataType.dt_int32); - else if (typeof(T) == typeof(Int2)) - (arity, dataType) = (2, DataType.dt_int32); - else if (typeof(T) == typeof(Int3)) - (arity, dataType) = (3, DataType.dt_int32); - else if (typeof(T) == typeof(Int4)) - (arity, dataType) = (4, DataType.dt_int32); - else if (typeof(T) == typeof(ulong)) - (arity, dataType) = (1, DataType.dt_uint64); - else if (typeof(T) == typeof(long)) - (arity, dataType) = (1, DataType.dt_int64); - else if (typeof(T) == typeof(float)) - (arity, dataType) = (1, DataType.dt_float32); - else if (typeof(T) == typeof(Vector2)) - (arity, dataType) = (2, DataType.dt_float32); - else if (typeof(T) == typeof(Vector3)) - (arity, dataType) = (3, DataType.dt_float32); - else if (typeof(T) == typeof(Vector4)) - (arity, dataType) = (4, DataType.dt_float32); - else if (typeof(T) == typeof(Matrix4x4)) - (arity, dataType) = (16, DataType.dt_float32); - else if (typeof(T) == typeof(double)) - (arity, dataType) = (1, DataType.dt_float64); - else if (typeof(T) == typeof(DVector2)) - (arity, dataType) = (2, DataType.dt_float64); - else if (typeof(T) == typeof(DVector3)) - (arity, dataType) = (3, DataType.dt_float64); - else if (typeof(T) == typeof(DVector4)) - (arity, dataType) = (4, DataType.dt_float64); - else - throw new Exception($"Unsupported data type {typeof(T)}"); - - // Check that the computed data type is consistent with the descriptor - if (dataType != Descriptor.DataType) - throw new Exception($"DataType was {dataType} but expected {Descriptor.DataType}"); - - // Check that the computed data arity is consistent with the descriptor - if (arity != Descriptor.DataArity) - throw new Exception($"DatArity was {arity} but expected {Descriptor.DataArity}"); - } - - public override GeometryAttribute Merge(IEnumerable others) - { - if (!others.Any()) - return this; - - // Check that all attributes have the same descriptor - if (!others.All(ma => ma.Descriptor.Equals(Descriptor))) - throw new Exception($"All attributes have to have same descriptor {Descriptor} to be concatenated"); - - // Check that all attributes have the same type - if (!others.All(ma => ma is GeometryAttribute)) - throw new Exception($"All attributes have to have the same type {typeof(T)} to be concatenated"); - - // Given multiple attributes associated with "all" or with "nothing", the first one takes precedence - if (Descriptor.Association == Association.assoc_all || Descriptor.Association == Association.assoc_none) - return this; - - // Sub-geometry attributes can't be merged - if (Descriptor.Association == Association.assoc_mesh) - throw new Exception("Can't merge sub-geometry attributes"); - - // Instance attributes can't be merged - if (Descriptor.Association == Association.assoc_instance) - throw new Exception("Can't merge instance attributes"); - - // Index attributes can't be merged - if (Descriptor.Semantic == Semantic.Index) - throw new Exception("Can't merge index attributes"); - - return others - .Select(ma => ma as GeometryAttribute) - .Prepend(this) - .ToIArray() - .Select(attr => attr.Data) - .Flatten() - .ToAttribute(Descriptor); - } - - public override GeometryAttribute Remap(IArray indices) - => Data.SelectByIndex(indices).ToAttribute(Descriptor); - - public override INamedBuffer ToBuffer() - => Data.ToArray().ToNamedBuffer(Name); - - public override GeometryAttribute Read(Stream stream, long byteCount) - { - if (byteCount % Descriptor.DataElementSize != 0) - throw new Exception($"The number of bytes to read {byteCount} does not divide cleanly by the size of the elements {Descriptor.DataElementSize}"); - var nElements = byteCount / Descriptor.DataElementSize; - if (nElements > int.MaxValue) - throw new Exception($"Trying to read {nElements} which is more than the maximum number of elements in a C# array"); - var data = stream.ReadArray((int)nElements); - return new GeometryAttribute(data.ToIArray(), Descriptor); - } - - public override GeometryAttribute SetIndex(int index) - => index == Descriptor.Index ? this : new GeometryAttribute(Data, Descriptor.SetIndex(index)); - } -} diff --git a/src/cs/g3d/Vim.G3d/GeometryAttributes.cs b/src/cs/g3d/Vim.G3d/GeometryAttributes.cs deleted file mode 100644 index ac799c07..00000000 --- a/src/cs/g3d/Vim.G3d/GeometryAttributes.cs +++ /dev/null @@ -1,164 +0,0 @@ -/* - G3D Geometry Format Library - Copyright 2019, VIMaec LLC. - Copyright 2018, Ara 3D Inc. - Usage licensed under terms of MIT License -*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using Vim.LinqArray; - -namespace Vim.G3d -{ - /// - /// This is the most generic representation of a G3D, which is a bag of attributes, and the observed size of each component. - /// - public class GeometryAttributes : IGeometryAttributes - { - public int NumCornersPerFace { get; } = -1; - - public int NumVertices { get; } = -1; - public int NumFaces { get; } = -1; - public int NumCorners { get; } = -1; - public int NumMeshes { get; } = -1; - public int NumInstances { get; } = -1; - public int NumMaterials { get; } = -1; - public int NumSubmeshes { get; } = -1; - - public int NumShapeVertices { get; } = -1; - public int NumShapes { get; } = -1; - - public IArray Attributes { get; } - - public GeometryAttribute GetAttribute(string name) - => Lookup.TryGetValue(name, out var val) ? val : null; - - public Dictionary Lookup - = new Dictionary(); - - public int ValidateAttribute(GeometryAttribute attr, int expectedCount) - { - if (expectedCount >= 0 && attr.ElementCount != expectedCount) - throw new Exception($"Attribute {attr.Descriptor.Name} size {attr.ElementCount} not match the expected size {expectedCount}"); - return attr.ElementCount; - } - - public GeometryAttributes(IEnumerable attributes, int numCornersPerFaceOverride = -1) - { - foreach (var attr in attributes) - { - if (attr != null && !Lookup.ContainsKey(attr.Name)) - Lookup.Add(attr.Name, attr); - } - - foreach (var attr in Lookup.Values) - { - var desc = attr.Descriptor; - - switch (desc.Association) - { - case Association.assoc_none: - break; - case Association.assoc_vertex: - NumVertices = ValidateAttribute(attr, NumVertices); - break; - case Association.assoc_edge: - case Association.assoc_corner: - NumCorners = ValidateAttribute(attr, NumCorners); - break; - case Association.assoc_face: - NumFaces = ValidateAttribute(attr, NumFaces); - break; - case Association.assoc_instance: - NumInstances = ValidateAttribute(attr, NumInstances); - break; - case Association.assoc_submesh: - NumSubmeshes = ValidateAttribute(attr, NumSubmeshes); - break; - case Association.assoc_material: - NumMaterials = ValidateAttribute(attr, NumMaterials); - break; - case Association.assoc_mesh: - NumMeshes = ValidateAttribute(attr, NumMeshes); - break; - case Association.assoc_shapevertex: - NumShapeVertices = ValidateAttribute(attr, NumShapeVertices); - break; - case Association.assoc_shape: - NumShapes = ValidateAttribute(attr, NumShapes); - break; - } - - if (desc.Semantic == Semantic.FaceSize) - { - if (desc.Association != Association.assoc_all) - throw new Exception($"The face size semantic has to be associated with entire geometry set, not {desc.Association}"); - if (desc.DataArity != 1) - throw new Exception($"The face size semantic has to have arity of 1, not {desc.DataArity}"); - - if (desc.DataType == DataType.dt_int8) - NumCornersPerFace = attr.AsType().Data[0]; - else if (desc.DataType == DataType.dt_int16) - NumCornersPerFace = attr.AsType().Data[0]; - else if (desc.DataType == DataType.dt_int32) - NumCornersPerFace = attr.AsType().Data[0]; - else - throw new Exception($"The face size semantic has to be an int8, int16, or int32"); - } - } - - if (NumVertices < 0) NumVertices = 0; - - // If the index attribute is missing we will have to add it. - if (!Lookup.ContainsKey(CommonAttributes.Index)) - { - if (NumCorners < 0) NumCorners = NumVertices; - Lookup.Add(CommonAttributes.Index, NumCorners.Range().ToIndexAttribute()); - } - - // Now we create the public ordered list of attributes - Attributes = Lookup.Values.OrderBy(attr => attr.Name).ToIArray(); - - - // If the number of corner and faces are observed, one has to be a multiple of the other - if (NumCorners > 0 && NumFaces > 0) - { - if (NumCorners % NumFaces != 0) - throw new Exception($"The number of corners {NumCorners} to be divisible by the number of faces {NumFaces}"); - } - - // Try to compute the number of corners per face - if (NumCornersPerFace < 0) - { - if (NumCorners > 0 && NumFaces > 0) - { - // We compute the number of corners per face by dividing the observed number of faces by the observed number of corners - NumCornersPerFace = NumCorners / NumFaces; - } - else - { - // By default we assume a triangular mesh - NumCornersPerFace = 3; - } - } - - if (numCornersPerFaceOverride >= 0) - { - NumCornersPerFace = numCornersPerFaceOverride; - } - - if (NumCorners < 0) NumCorners = NumVertices; - if (NumInstances < 0) NumInstances = 0; - if (NumMeshes < 0) NumMeshes = 0; - if (NumFaces < 0) NumFaces = NumCorners / NumCornersPerFace; - - if (NumShapeVertices < 0) NumShapeVertices = 0; - if (NumShapes < 0) NumShapes = 0; - } - - public static GeometryAttributes Empty - => new GeometryAttributes(new GeometryAttribute[] { }); - } -} diff --git a/src/cs/g3d/Vim.G3d/GeometryAttributesExtensions.cs b/src/cs/g3d/Vim.G3d/GeometryAttributesExtensions.cs deleted file mode 100644 index ba7c1cfa..00000000 --- a/src/cs/g3d/Vim.G3d/GeometryAttributesExtensions.cs +++ /dev/null @@ -1,520 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.G3d -{ - public static class GeometryAttributesExtensions - { - public static int ExpectedElementCount(this IGeometryAttributes self, AttributeDescriptor desc) - { - switch (desc.Association) - { - case Association.assoc_all: - return 1; - case Association.assoc_none: - return 0; - case Association.assoc_vertex: - return self.NumVertices; - case Association.assoc_face: - return self.NumFaces; - case Association.assoc_corner: - return self.NumCorners; - case Association.assoc_edge: - return self.NumCorners; - case Association.assoc_mesh: - return self.NumMeshes; - case Association.assoc_instance: - return self.NumInstances; - case Association.assoc_shapevertex: - return self.NumShapeVertices; - case Association.assoc_shape: - return self.NumShapes; - } - return -1; - } - - public static IArray AttributeNames(this IGeometryAttributes g) - => g.Attributes.Select(attr => attr.Name); - - public static GeometryAttribute GetAttribute(this IGeometryAttributes g, string attributeName) where T : unmanaged - => g.GetAttribute(attributeName)?.AsType(); - - public static GeometryAttribute DefaultAttribute(this IGeometryAttributes self, string name) - => self.DefaultAttribute(AttributeDescriptor.Parse(name)); - - public static GeometryAttribute DefaultAttribute(this IGeometryAttributes self, AttributeDescriptor desc) - => desc.ToDefaultAttribute(self.ExpectedElementCount(desc)); - - public static GeometryAttribute GetOrDefaultAttribute(this IGeometryAttributes self, AttributeDescriptor desc) - => self.GetAttribute(desc.ToString()) ?? desc.ToDefaultAttribute(self.ExpectedElementCount(desc)); - - public static IEnumerable NoneAttributes(this IGeometryAttributes g) - => g.Attributes.Where(a => a.Descriptor.Association == Association.assoc_none); - - public static IEnumerable CornerAttributes(this IGeometryAttributes g) - => g.Attributes.Where(a => a.Descriptor.Association == Association.assoc_corner); - - public static IEnumerable EdgeAttributes(this IGeometryAttributes g) - => g.Attributes.Where(a => a.Descriptor.Association == Association.assoc_edge); - - public static IEnumerable FaceAttributes(this IGeometryAttributes g) - => g.Attributes.Where(a => a.Descriptor.Association == Association.assoc_face); - - public static IEnumerable VertexAttributes(this IGeometryAttributes g) - => g.Attributes.Where(a => a.Descriptor.Association == Association.assoc_vertex); - - public static IEnumerable InstanceAttributes(this IGeometryAttributes g) - => g.Attributes.Where(a => a.Descriptor.Association == Association.assoc_instance); - - public static IEnumerable MeshAttributes(this IGeometryAttributes g) - => g.Attributes.Where(a => a.Descriptor.Association == Association.assoc_mesh); - - public static IEnumerable SubMeshAttributes(this IGeometryAttributes g) - => g.Attributes.Where(a => a.Descriptor.Association == Association.assoc_submesh); - - public static IEnumerable WholeGeometryAttributes(this IGeometryAttributes g) - => g.Attributes.Where(a => a.Descriptor.Association == Association.assoc_all); - - public static IEnumerable ShapeVertexAttributes(this IGeometryAttributes g) - => g.Attributes.Where(a => a.Descriptor.Association == Association.assoc_shapevertex); - - public static IEnumerable ShapeAttributes(this IGeometryAttributes g) - => g.Attributes.Where(a => a.Descriptor.Association == Association.assoc_shape); - - public static bool HasSameAttributes(this IGeometryAttributes g1, IGeometryAttributes g2) - => g1.Attributes.Count == g2.Attributes.Count && g1.Attributes.Indices().All(i => g1.Attributes[i].Name == g2.Attributes[i].Name); - - public static int FaceToCorner(this IGeometryAttributes g, int f) - => f * g.NumCornersPerFace; - - /// - /// Given a set of face indices, creates an array of corner indices - /// - public static IArray FaceIndicesToCornerIndices(this IGeometryAttributes g, IArray faceIndices) - => (faceIndices.Count * g.NumCornersPerFace) - .Select(i => g.FaceToCorner(faceIndices[i / g.NumCornersPerFace]) + i % g.NumCornersPerFace); - - /// - /// Given a set of face indices, creates an array of indices of the first corner in each face - /// - public static IArray FaceIndicesToFirstCornerIndices(this IGeometryAttributes g, IArray faceIndices) - => faceIndices.Select(f => f * g.NumCornersPerFace); - - public static int CornerToFace(this IGeometryAttributes g, int c) - => c / g.NumCornersPerFace; - - public static IArray CornersToFaces(this IGeometryAttributes g) - => g.NumCorners.Select(g.CornerToFace); - - public static int CornerNumber(this IGeometryAttributes g, int c) - => c % g.NumCornersPerFace; - - public static IGeometryAttributes ToGeometryAttributes(this IEnumerable attributes) - => new GeometryAttributes(attributes); - - public static IGeometryAttributes ToGeometryAttributes(this IArray attributes) - => attributes.ToEnumerable().ToGeometryAttributes(); - - public static IGeometryAttributes AddAttributes(this IGeometryAttributes attributes, params GeometryAttribute[] newAttributes) - => attributes.Attributes.ToEnumerable().Concat(newAttributes).ToGeometryAttributes(); - - public static GeometryAttribute GetAttributeOrDefault(this IGeometryAttributes g, string name) - => g.GetAttribute(name) ?? g.DefaultAttribute(name); - - public static IGeometryAttributes Merge(this IArray gs) - => gs.Select(x => (IGeometryAttributes)x).Merge(); - - public static IGeometryAttributes Merge(this IGeometryAttributes self, params IGeometryAttributes[] gs) - => gs.ToIArray().Prepend(self).Merge(); - - public static IGeometryAttributes Merge(this IArray geometryAttributesArray) - { - if (geometryAttributesArray.Count == 0) - return GeometryAttributes.Empty; - - var first = geometryAttributesArray[0]; - - if (geometryAttributesArray.Count == 1) - return first; - var corners = first.NumCornersPerFace; - if (!geometryAttributesArray.All(g => g.NumCornersPerFace == corners)) - throw new Exception("Cannot merge meshes with different numbers of corners per faces"); - - // Merge all of the attributes of the different geometries - // Except: indices, group indexes, subgeo, and instance attributes - var attributes = first.VertexAttributes() - .Concat(first.CornerAttributes()) - .Concat(first.EdgeAttributes()) - .Concat(first.NoneAttributes()) - .Concat(first.FaceAttributes()) - .Append(first.GetAttributeSubmeshMaterial()) - // Skip the index semantic because things get re-ordered - .Where(attr => attr != null && attr.Descriptor.Semantic != Semantic.Index) - .ToArray(); - - // Merge the non-indexed attributes - var others = geometryAttributesArray.Skip(1).ToEnumerable(); - var attributeList = attributes.Select( - attr => attr.Merge(others.Select(g => g.GetAttributeOrDefault(attr.Name)))).ToList(); - - // Merge the index attribute - // numVertices: [X], [Y], [Z], ... - // valueOffsets: [0], [X], [X+Y], ... - // indices: [A, B, C], [D, E, F], [G, H, I], ... - // mergedIndices: [A, B, C], [X+D, X+E, X+F], [X+Y+G, X+Y+H, X+Y+I], ... - var mergedIndexAttribute = geometryAttributesArray.MergeIndexedAttribute( - ga => ga.GetAttributeIndex(), - ga => ga.NumVertices) - ?.ToIndexAttribute(); - - if (mergedIndexAttribute != null) - attributeList.Add(mergedIndexAttribute); - - // Merge the submesh index offset attribute - // numCorners: [X], [Y], [Z], ... - // valueOffsets: [0] [X], [X+Y], ... - // submeshIndexOffsets: [0, A, B], [0, C, D], [0, E, F], ... - // mergedSubmeshIndexOffsets: [0, A, B], [X, X+C, X+D], [X+Y, X+Y+E, X+Y+F], ... - var mergedSubmeshIndexOffsetAttribute = geometryAttributesArray.MergeIndexedAttribute( - ga => ga.GetAttributeSubmeshIndexOffset(), - ga => ga.NumCorners) - ?.ToSubmeshIndexOffsetAttribute(); - - if (mergedSubmeshIndexOffsetAttribute != null) - attributeList.Add(mergedSubmeshIndexOffsetAttribute); - - return attributeList.ToGeometryAttributes(); - } - - /// - /// Merges the indexed attributes based on the given transformations and returns an array of integers - /// representing the merged and offset values. - /// - public static int[] MergeIndexedAttribute( - this IArray geometryAttributesArray, - Func> getIndexedAttributeFunc, - Func getValueOffsetFunc, - int initialValueOffset = 0) - { - var first = geometryAttributesArray.FirstOrDefault(); - if (first == null) - return null; - - var firstAttribute = getIndexedAttributeFunc(first); - if (firstAttribute == null) - return null; - - return geometryAttributesArray.MergeAttributes( - getIndexedAttributeFunc, - tuples => - { - var valueOffset = initialValueOffset; - var mergedCount = 0; - - var merged = new int[tuples.Sum(t => t.Attribute.Data.Count)]; - - foreach (var (parent, attr) in tuples) - { - var attrData = attr.Data; - var attrDataCount = attr.Data.Count; - - for (var i = 0; i < attrDataCount; ++i) - merged[mergedCount + i] = attrData[i] + valueOffset; - - mergedCount += attrDataCount; - valueOffset += getValueOffsetFunc(parent); - } - - return merged; - }); - } - - /// - /// Merges the attributes based on the given transformations and returns an array of merged values. - /// - public static T[] MergeAttributes( - this IArray geometryAttributesArray, - Func> getAttributeFunc, - Func<(IGeometryAttributes Parent, GeometryAttribute Attribute)[], T[]> mergeFunc) where T : unmanaged - { - var tuples = geometryAttributesArray - .Select(ga => (Parent: ga, GeometryAttribute: getAttributeFunc(ga))) - .Where(tuple => tuple.GeometryAttribute != null) - .ToArray(); - - if (tuples.Length != geometryAttributesArray.Count) - throw new Exception("The geometry attributes array do not all contain the same attribute"); - - return mergeFunc(tuples); - } - - /// - /// Applies a transformation function to position attributes and another to normal attributes. When deforming, we may want to - /// apply a similar deformation to the normals. For example a matrix can change the position, rotation, and scale of a geometry, - /// but the only changes should be to the direction of the normal, not the length. - /// - public static IGeometryAttributes Deform(this IGeometryAttributes g, Func positionTransform, Func normalTransform) - => g.Attributes.Select( - a => - (a.Descriptor.Semantic == Semantic.Position && a is GeometryAttribute p) ? p.Data.Select(positionTransform).ToAttribute(a.Descriptor) : - (a.Descriptor.Semantic == Semantic.Normal && a is GeometryAttribute n) ? n.Data.Select(normalTransform).ToAttribute(a.Descriptor) : - a) - .ToGeometryAttributes(); - - /// - /// Applies a deformation to points, without changing the normals. For some transformation functions this can result in incorrect normals. - /// - public static IGeometryAttributes Deform(this IGeometryAttributes g, Func positionTransform) - => g.Attributes.Select( - a => - (a.Descriptor.Semantic == Semantic.Position && a is GeometryAttribute p) ? p.Data.Select(positionTransform).ToAttribute(a.Descriptor) : - a) - .ToGeometryAttributes(); - - /// - /// Applies a transformation matrix - /// - public static IGeometryAttributes Transform(this IGeometryAttributes g, Matrix4x4 matrix) - => g.Deform(v => v.Transform(matrix), v => v.TransformNormal(matrix)); - - public static IGeometryAttributes SetPosition(this IGeometryAttributes g, IArray points) - => g.SetAttribute(points.ToPositionAttribute()); - - public static IGeometryAttributes SetAttribute(this IGeometryAttributes self, GeometryAttribute attr) - => self.Attributes.Where(a => !a.Descriptor.Equals(attr.Descriptor)).Append(attr).ToGeometryAttributes(); - - public static IGeometryAttributes SetAttribute(this IGeometryAttributes self, IArray values, AttributeDescriptor desc) where ValueT : unmanaged - => self.SetAttribute(values.ToAttribute(desc)); - - /// - /// Leaves the vertex buffer intact and creates a new geometry that remaps all of the group, corner, and face data. - /// The newFaces array is a list of indices into the old face array. - /// Note: meshes are lost. - /// - public static IGeometryAttributes RemapFaces(this IGeometryAttributes g, IArray faceRemap) - => g.RemapFacesAndCorners(faceRemap, g.FaceIndicesToCornerIndices(faceRemap)); - - public static IEnumerable SetFaceSizeAttribute(this IEnumerable attributes, int numCornersPerFaces) - => (numCornersPerFaces <= 0) - ? attributes - : attributes - .Where(attr => attr.Descriptor.Semantic != Semantic.FaceSize) - .Append(new[] { numCornersPerFaces }.ToObjectFaceSizeAttribute()); - - /// - /// Low-level remap function. Maps faces and corners at the same time. - /// In some cases, this is important (e.g. triangulating quads). - /// Note: meshes are lost. - /// - public static IGeometryAttributes RemapFacesAndCorners(this IGeometryAttributes g, IArray faceRemap, IArray cornerRemap, int numCornersPerFace = -1) - => g.VertexAttributes() - .Concat(g.NoneAttributes()) - .Concat(g.FaceAttributes().Select(attr => attr.Remap(faceRemap))) - .Concat(g.EdgeAttributes().Select(attr => attr.Remap(cornerRemap))) - .Concat(g.CornerAttributes().Select(attr => attr.Remap(cornerRemap))) - .Concat(g.WholeGeometryAttributes()) - .SetFaceSizeAttribute(numCornersPerFace) - .ToGeometryAttributes(); - - /// - /// Converts a quadrilateral mesh into a triangular mesh carrying over all attributes. - /// - public static IGeometryAttributes TriangulateQuadMesh(this IGeometryAttributes g) - { - if (g.NumCornersPerFace != 4) throw new Exception("Not a quad mesh"); - - var cornerRemap = new int[g.NumFaces * 6]; - var faceRemap = new int[g.NumFaces * 2]; - var cur = 0; - for (var i = 0; i < g.NumFaces; ++i) - { - cornerRemap[cur++] = i * 4 + 0; - cornerRemap[cur++] = i * 4 + 1; - cornerRemap[cur++] = i * 4 + 2; - cornerRemap[cur++] = i * 4 + 0; - cornerRemap[cur++] = i * 4 + 2; - cornerRemap[cur++] = i * 4 + 3; - - faceRemap[i * 2 + 0] = i; - faceRemap[i * 2 + 1] = i; - } - - return g.RemapFacesAndCorners(faceRemap.ToIArray(), cornerRemap.ToIArray(), 3); - } - - public static IGeometryAttributes CopyFaces(this IGeometryAttributes g, IArray keep) - => g.CopyFaces(i => keep[i]); - - public static IGeometryAttributes CopyFaces(this IGeometryAttributes g, IArray keep) - => g.RemapFaces(keep); - - public static IGeometryAttributes CopyFaces(this IGeometryAttributes self, Func predicate) - => self.RemapFaces(self.NumFaces.Select(i => i).IndicesWhere(predicate).ToIArray()); - - public static IGeometryAttributes DeleteFaces(this IGeometryAttributes g, Func predicate) - => g.CopyFaces(i => !predicate(i)); - - public static IGeometryAttributes CopyFaces(this IGeometryAttributes g, int from, int count) - => g.CopyFaces(i => i >= from && i < from + count); - - /// - /// Updates the vertex buffer (e.g. after identifying unwanted faces) and the index - /// buffer. Vertices are either re-ordered, removed, or deleted. Does not affect any other - /// - public static IGeometryAttributes RemapVertices(this IGeometryAttributes g, IArray newVertices, IArray newIndices) - => (new[] { newIndices.ToIndexAttribute() } - .Concat( - g.VertexAttributes() - .Select(attr => attr.Remap(newVertices))) - .Concat(g.NoneAttributes()) - .Concat(g.FaceAttributes()) - .Concat(g.EdgeAttributes()) - .Concat(g.CornerAttributes()) - .Concat(g.WholeGeometryAttributes()) - ) - .ToGeometryAttributes(); - - /// - /// The vertRemap is a list of vertices in the new vertex buffer, and where they came from. - /// This could be a reordering of the original vertex buffer, it could even be a repetition. - /// It could also be some vertices were deleted, BUT if those vertices are still referenced - /// then this will throw an exception. - /// The values in the index buffer will change, but it will stay the same length. - /// - public static IGeometryAttributes RemapVertices(this IGeometryAttributes g, IArray vertRemap) - { - var vertLookup = (-1).Repeat(g.NumVertices).ToArray(); - for (var i = 0; i < vertRemap.Count; ++i) - { - var oldVert = vertRemap[i]; - vertLookup[oldVert] = i; - } - - var oldIndices = g.GetAttributeIndex()?.Data ?? g.NumVertices.Range(); - var newIndices = oldIndices.Select(i => vertLookup[i]).Evaluate(); - - if (newIndices.Any(x => x == -1)) - throw new Exception("At least one of the indices references a vertex that no longer exists"); - - return g.RemapVertices(vertRemap, newIndices); - } - - /// - /// For mesh g, create a new mesh from the passed selected faces, - /// discarding un-referenced data and generating new index, vertex & face buffers. - /// - public static IGeometryAttributes SelectFaces(this IGeometryAttributes g, IArray faces) - { - // Early exit, if all selected no need to do anything - if (g.NumFaces == faces.Count) - return g; - - // Early exit, if none selected no need to do anything - if (faces.Count == 0) - return null; - - // First, get all the indices for this array of faces - var oldIndices = g.GetAttributeIndex(); - var oldSelIndices = new int[faces.Count * g.NumCornersPerFace]; - // var oldIndices = faces.SelectMany(f => f.Indices()); - for (var i = 0; i < faces.Count; i++) - { - for (var t = 0; t < 3; t++) - oldSelIndices[i * 3 + t] = oldIndices.Data[faces[i] * g.NumCornersPerFace + t]; - } - - // We need to create list of newIndices, and remapping - // of oldVertices to newVertices - var newIndices = (-1).Repeat(oldSelIndices.Length).ToArray(); - // Each index could potentially be in the new mesh - var indexLookup = (-1).Repeat(oldIndices.ElementCount).ToArray(); - - // Build mapping. For each index, if the vertex it has - // already been referred to has been mapped, use the - // mapped index. Otherwise, remember that we need this vertex - var numUsedVertices = 0; - // remapping from old vert array => new vert array - // Cache built of the old indices of vertices that are used - // should be equivalent to indexLookup.Where(i => i != -1) - var oldVertices = g.GetAttributePosition(); - var usedVertices = (-1).Repeat(oldVertices.ElementCount).ToArray(); - for (var i = 0; i < oldSelIndices.Length; ++i) - { - var oldIndex = oldSelIndices[i]; - var newIndex = indexLookup[oldIndex]; - if (newIndex < 0) - { - // remapping from old vert array => new vert array - usedVertices[numUsedVertices] = oldIndex; - newIndex = indexLookup[oldIndex] = numUsedVertices++; - } - newIndices[i] = newIndex; - } - - //var faceRemapping = faces.Select(f => f.Index); - var vertRemapping = usedVertices.Take(numUsedVertices).ToIArray(); - var cornerRemapping = g.FaceIndicesToCornerIndices(faces); - - return g.VertexAttributes() - .Select(attr => attr.Remap(vertRemapping)) - .Concat(g.NoneAttributes()) - .Concat(g.FaceAttributes().Select(attr => attr.Remap(faces))) - .Concat(g.EdgeAttributes().Select(attr => attr.Remap(cornerRemapping))) - .Concat(g.CornerAttributes().Select(attr => attr.Remap(cornerRemapping))) - .Concat(g.WholeGeometryAttributes()) - .ToGeometryAttributes() - .SetAttribute(newIndices.ToIndexAttribute()); - } - - public static G3D ToG3d(this IEnumerable attributes, G3dHeader? header = null) - => new G3D(attributes, header); - - public static G3D ToG3d(this IArray attributes, G3dHeader? header = null) - => attributes.ToEnumerable().ToG3d(header); - - public static IArray IndexFlippedRemapping(this IGeometryAttributes g) - => g.NumCorners.Select(c => ((c / g.NumCornersPerFace) + 1) * g.NumCornersPerFace - 1 - c % g.NumCornersPerFace); - - public static bool IsNormalAttribute(this GeometryAttribute attr) - => attr.IsType() && attr.Descriptor.Semantic == "normal"; - - public static IEnumerable FlipNormalAttributes(this IEnumerable self) - => self.Select(attr => attr.IsNormalAttribute() - ? attr.AsType().Data.Select(v => v.Inverse()).ToAttribute(attr.Descriptor) - : attr); - - public static IGeometryAttributes FlipWindingOrder(this IGeometryAttributes g) - => g.VertexAttributes() - .Concat(g.NoneAttributes()) - .Concat(g.FaceAttributes()) - .Concat(g.EdgeAttributes().Select(attr => attr.Remap(g.IndexFlippedRemapping()))) - .Concat(g.CornerAttributes().Select(attr => attr.Remap(g.IndexFlippedRemapping()))) - .Concat(g.WholeGeometryAttributes()) - .FlipNormalAttributes() - .ToGeometryAttributes(); - - public static IGeometryAttributes DoubleSided(this IGeometryAttributes g) - => g.Merge(g.FlipWindingOrder()); - - public static IArray DefaultMaterials(this IGeometryAttributes self) - => (-1).Repeat(self.NumFaces); - - public static IArray DefaultColors(this IGeometryAttributes self) - => Vector4.Zero.Repeat(self.NumVertices); - - public static IArray DefaultUvs(this IGeometryAttributes self) - => Vector2.Zero.Repeat(self.NumVertices); - - public static IGeometryAttributes Replace(this IGeometryAttributes self, Func selector, GeometryAttribute attribute) - => self.Attributes.Where(a => !selector(a.Descriptor)).Append(attribute).ToGeometryAttributes(); - - public static IGeometryAttributes Remove(this IGeometryAttributes self, Func selector) - => self.Attributes.Where(a => !selector(a.Descriptor)).ToGeometryAttributes(); - - public static G3D ToG3d(this IGeometryAttributes self) - => G3D.Create(self.Attributes.ToArray()); - } -} diff --git a/src/cs/g3d/Vim.G3d/Header.cs b/src/cs/g3d/Vim.G3d/Header.cs deleted file mode 100644 index c37140ce..00000000 --- a/src/cs/g3d/Vim.G3d/Header.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Text; - -namespace Vim.G3d -{ - // http://docs.autodesk.com/FBX/2014/ENU/FBX-SDK-Documentation/index.html?url=files/GUID-CC93340E-C4A1-49EE-B048-E898F856CFBF.htm,topicNumber=d30e8478 - // https://twitter.com/FreyaHolmer/status/644881436982575104 - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#coordinate-system-and-units - - // The header is 7 bytes + 1 bytes padding. - public struct G3dHeader - { - public const byte MagicA = 0x63; - public const byte MagicB = 0xD0; - - public byte magicA; // 0x63 - public byte magicB; // 0xD0 - public byte unitA; // with unitB could be: 'ft', 'yd', 'mi', 'km', 'mm', 'in', 'cm', 'm', - public byte unitB; - public byte upAxis; // e.g. 1=y or 2=z (could be 0=x, if you hate people) - public byte forwardVector; // e.g. 0=x, 1=y, 2=z, 3=-x, 4=-y, 5=-z - public byte handedness; // 0=left-handed, 1=right-handed - public byte padding; // 0 - - public string Unit => Encoding.ASCII.GetString(new byte[] { unitA, unitB }); - - public byte[] ToBytes() - => new[] { magicA, magicB, unitA, unitB, upAxis, forwardVector, handedness, padding }; - - public static G3dHeader FromBytes(byte[] bytes) - => new G3dHeader - { - magicA = bytes[0], - magicB = bytes[1], - unitA = bytes[2], - unitB = bytes[3], - upAxis = bytes[4], - forwardVector = bytes[5], - handedness = bytes[6], - } - .Validate(); - - public static G3dHeader Default - = new G3dHeader - { - magicA = 0x63, - magicB = 0xD0, - unitA = (byte)'m', - unitB = 0, - upAxis = 2, - forwardVector = 0, - handedness = 0, - padding = 0 - }; - - public static readonly string[] SupportedUnits = { "mm", "cm", "m\0", "km", "in", "ft", "yd", "mi" }; - - public G3dHeader Validate() - { - if (magicA != 0x63) throw new Exception($"First magic number must be 0x63 not {magicA}"); - if (magicB != 0xD0) throw new Exception($"Second magic number must be 0xD0 not {magicB}"); - if (Array.IndexOf(SupportedUnits, Unit) < 0) throw new Exception($"Unit {Unit} is not a supported unit: {string.Join(", ", SupportedUnits)}"); - if (upAxis < 0 || upAxis > 2) throw new Exception("Up axis must be 0(x), 1(y), or 2(z)"); - if (forwardVector < 0 || forwardVector > 5) throw new Exception("Front vector must be 0 (x), 1(y), 2(z), 3(-x), 4(-y), or 5(-z)"); - if (handedness < 0 || handedness > 1) throw new Exception("Handedness must be 0 (left) or 1 (right"); - return this; - } - } -} diff --git a/src/cs/g3d/Vim.G3d/IGeometryAttributes.cs b/src/cs/g3d/Vim.G3d/IGeometryAttributes.cs deleted file mode 100644 index e25da638..00000000 --- a/src/cs/g3d/Vim.G3d/IGeometryAttributes.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Vim.LinqArray; - -namespace Vim.G3d -{ - /// - /// This is a read-only collection of G3D attributes. - /// - public interface IGeometryAttributes - { - int NumCornersPerFace { get; } - int NumVertices { get; } - int NumCorners { get; } - int NumFaces { get; } - int NumInstances { get; } - int NumMeshes { get; } - int NumShapeVertices { get; } - int NumShapes { get; } - - IArray Attributes { get; } - GeometryAttribute GetAttribute(string name); - } -} diff --git a/src/cs/g3d/Vim.G3d/MetaHeader.cs b/src/cs/g3d/Vim.G3d/MetaHeader.cs new file mode 100644 index 00000000..b79db7ea --- /dev/null +++ b/src/cs/g3d/Vim.G3d/MetaHeader.cs @@ -0,0 +1,63 @@ +using System; +using System.Text; + +namespace Vim.G3d +{ + // http://docs.autodesk.com/FBX/2014/ENU/FBX-SDK-Documentation/index.html?url=files/GUID-CC93340E-C4A1-49EE-B048-E898F856CFBF.htm,topicNumber=d30e8478 + // https://twitter.com/FreyaHolmer/status/644881436982575104 + // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#coordinate-system-and-units + + public struct MetaHeader + { + public byte MagicA; // 0x63 + public byte MagicB; // 0xD0 + public byte UnitA; // with unitB could be: 'ft', 'yd', 'mi', 'km', 'mm', 'in', 'cm', 'm', + public byte UnitB; + public byte UpAxis; // e.g. 1=y or 2=z (could be 0=x, if you hate people) + public byte ForwardVector; // e.g. 0=x, 1=y, 2=z, 3=-x, 4=-y, 5=-z + public byte Handedness; // 0=left-handed, 1=right-handed + public byte Padding; // 0 + + public string Unit => Encoding.ASCII.GetString(new byte[] { UnitA, UnitB }); + + public byte[] ToBytes() + => new[] { MagicA, MagicB, UnitA, UnitB, UpAxis, ForwardVector, Handedness, Padding }; + + public static MetaHeader FromBytes(byte[] bytes) + => new MetaHeader + { + MagicA = bytes[0], + MagicB = bytes[1], + UnitA = bytes[2], + UnitB = bytes[3], + UpAxis = bytes[4], + ForwardVector = bytes[5], + Handedness = bytes[6], + } + .Validate(); + + public static MetaHeader Default + = new MetaHeader + { + MagicA = Constants.MetaHeaderMagicA, + MagicB = Constants.MetaHeaderMagicB, + UnitA = (byte)'m', + UnitB = 0, + UpAxis = 2, + ForwardVector = 0, + Handedness = 0, + Padding = 0 + }; + + public MetaHeader Validate() + { + if (MagicA != Constants.MetaHeaderMagicA) throw new Exception($"First magic number must be 0x{Constants.MetaHeaderMagicA:X2} not 0x{MagicA:X2}"); + if (MagicB != Constants.MetaHeaderMagicB) throw new Exception($"Second magic number must be 0x{Constants.MetaHeaderMagicB:X2} not 0x{MagicB:X2}"); + if (Array.IndexOf(Constants.MetaHeaderSupportedUnits, Unit) < 0) throw new Exception($"Unit {Unit} is not a supported unit: {string.Join(", ", Constants.MetaHeaderSupportedUnits)}"); + if (UpAxis < 0 || UpAxis > 2) throw new Exception("Up axis must be 0(x), 1(y), or 2(z)"); + if (ForwardVector < 0 || ForwardVector > 5) throw new Exception("Front vector must be 0 (x), 1(y), 2(z), 3(-x), 4(-y), or 5(-z)"); + if (Handedness < 0 || Handedness > 1) throw new Exception("Handedness must be 0 (left) or 1 (right"); + return this; + } + } +} diff --git a/src/cs/g3d/Vim.G3d/ObjExporter.cs b/src/cs/g3d/Vim.G3d/ObjExporter.cs deleted file mode 100644 index b76184dd..00000000 --- a/src/cs/g3d/Vim.G3d/ObjExporter.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.Collections.Generic; -using System.Text; -using System.IO; -using Vim.LinqArray; - -namespace Vim.G3d -{ - /// - /// This is a simple ObjExporter for the purposes of testing. - /// - public static class ObjExporter - { - public static IEnumerable ObjLines(G3D g3d) - { - // Write the vertices - var vertices = g3d.Vertices; - var uvs = g3d.VertexUvs; - foreach (var v in vertices.ToEnumerable()) - yield return ($"v {v.X} {v.Y} {v.Z}"); - if (uvs != null) - { - for (var v = 0; v < uvs.Count; v++) - yield return ($"vt {uvs[v].X} {uvs[v].Y}"); - } - - var indices = g3d.Indices; - var sb = new StringBuilder(); - var i = 0; - var faceSize = g3d.NumCornersPerFace; - while (i < indices.Count) - { - sb.Append("f"); - - if (uvs == null) - { - for (var j = 0; j < faceSize; ++j) - { - var index = g3d.Indices[i++] + 1; - sb.Append(" ").Append(index); - } - } - else - { - for (var j = 0; j < faceSize; ++j) - { - var index = g3d.Indices[i++] + 1; - sb.Append(" ").Append(index).Append("/").Append(index); - } - } - - yield return sb.ToString(); - sb.Clear(); - } - } - - public static void WriteObj(this G3D g3d, string filePath) - => File.WriteAllLines(filePath, ObjLines(g3d)); - } -} diff --git a/src/cs/g3d/Vim.G3d/PlyExporter.cs b/src/cs/g3d/Vim.G3d/PlyExporter.cs deleted file mode 100644 index 1b2f3425..00000000 --- a/src/cs/g3d/Vim.G3d/PlyExporter.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Collections.Generic; -using System.Text; -using System.IO; -using Vim.Math3d; - -namespace Vim.G3d -{ - /// - /// A very simple .ply file exporter - /// - public static class PlyExporter - { - public static void WritePly(this G3D g, string filePath) - => File.WriteAllLines(filePath, PlyStrings(g)); - - public static IEnumerable PlyStrings(G3D g) - { - var vertices = g.Vertices; - var indices = g.Indices; - var colors = g.VertexColors; - - //Write the header - yield return "ply"; - yield return "format ascii 1.0"; - yield return "element vertex " + vertices.Count + ""; - yield return "property float x"; - yield return "property float y"; - yield return "property float z"; - if (colors != null) - { - yield return "property uint8 red"; - yield return "property uint8 green"; - yield return "property uint8 blue"; - } - yield return "element face " + g.NumFaces; - yield return "property list uint8 int32 vertex_index"; - yield return "end_header"; - - // Write the vertices - if (colors != null) - { - for (var i = 0; i < vertices.Count; i++) - { - var v = vertices[i]; - var c = (colors[i] * 255f).Clamp(Vector4.Zero, new Vector4(255, 255, 255, 255)); - - yield return - $"{v.X} {v.Y} {v.Z} {(byte)c.X} {(byte)c.Y} {(byte)c.Z}"; - } - } - else - { - for (var i = 0; i < vertices.Count; i++) - { - var v = vertices[i]; - yield return - $"{v.X} {v.Y} {v.Z}"; - } - } - - // Write the face indices - var index = 0; - var sb = new StringBuilder(); - var faceSize = g.NumCornersPerFace; - for (var i = 0; i < g.NumFaces; i++) - { - sb.Append(faceSize); - for (var j = 0; j < faceSize; j++) - { - sb.Append(" ").Append(indices[index++]); - } - - yield return sb.ToString(); - sb.Clear(); - } - } - } -} diff --git a/src/cs/g3d/Vim.G3d/README.md b/src/cs/g3d/Vim.G3d/README.md deleted file mode 100644 index fd8f0ebe..00000000 --- a/src/cs/g3d/Vim.G3d/README.md +++ /dev/null @@ -1,109 +0,0 @@ -# G3D - -[](https://www.nuget.org/packages/Vim.G3d) - -G3D is a simple, efficient, generic binary format for storing and transmitting geometry. The G3D format -is designed to be used either as a serialization format or as an in-memory data structure. - -G3D can represent triangular meshes, quadrilateral meshes, polygonal meshes, point clouds, and line segments. -It can be easily and efficiently deserialized and rendered in different languages and on different platforms. - -The G3D format can contain a superset of geometry attributes found in most common geometry formats, -including formats such as FBX, glTF, OBJ, PLY, and in memory data structures used in popular 3D APIs, like -Unity, Three.JS, Assimp, and 3dsMax. - -BFAST is maintained by [VIMaec LLC](https://vimaec.com) and is licensed under the terms of the MIT License. - -# Format - -## BFAST Container - -The underlying binary layout of a G3D file conforms to the [BFAST serialization format](https://github.com/vimaec/bfast), which is a simple and efficient binary format for serializing -collections of byte arrays. BFAST provides an interface that allows named arrays of binary data to be serialized and deserialized quickly and easily. - -The first named buffer in the BFAST container is reserved for meta-information about the file encoded in JSON format. It has the name "meta". -Each subsequent buffer uses the attribute descriptor string as a name. - -## Meta-Information - -The first buffer of a G3D file is named "meta" and is represented by the G3D - -## Attributes - -G3D is organized as a collection of attribute buffers. Each attributes describe what part of the incoming geometry they are associated with: - -* point // vertex data -* corner // face-vertex data -* face // per polygon data -* edge // per half-edge data -* group // face group. Face groups are identified by a face data buffer -* SubGeometry // a contiguous section of the vertex-buffer, and index-buffer -* material // data associated with a material, materials are usually associated with groups, a -* instance // instance data, usually an instance has an index to a SubGeometry -* all // whole object data - for example face-size of 4 with whole object indicates a quad mesh -* none // no association - -Attributes also have a "semantic" which is used to identify what role the attribute has when parsing. These map roughly to FBX layer elements, or Three.JS buffer attributes. -There are a number of predefined semantic values with reserved names, but applications are free to define custom semantic values. The only required semantic in a G3D file is -"position". Here is a list of some of the predefined semantics: - -* unknown, // no known attribute type -* position, // vertex buffer -* index, // index buffer -* indexoffset, // an offset into the index buffer (used with Subgeometries) -* vertexoffset, // the offset into the vertex buffer (used only with Subgeometries) -* normal, // computed normal information (per face, group, corner, or vertex) -* binormal, // computed binormal information -* tangent, // computed tangent information -* material, // material index -* visibility, // visibility data (e.g. -* size, // number of indices per face or group -* uv, // UV (sometimes more than 1, e.g. Unity supports up to 8) -* color, // usually vertex color, but could be edge color as well -* smoothing, // identifies smoothing groups (e.g. ala 3ds Max and OBJ files) -* weight, // in 3ds Max this is called selection -* mapchannel, // 3ds Max map channel (assoc of none => map verts, assoc of corner => map faces) -* id, // used to identify what object each face part came from -* joint, // used to identify what a joint a skin is associated with -* boxes, // used to identify bounding boxes -* spheres, // used to identify bounding spheres -* user, // identifies user specific data (in 3ds Max this could be "per-vertex-data") - -Attributes are stored in 512-byte aligned data-buffers arranged as arrays of scalars or fixed width vectors. The individual data values can be integers, or floating point values of various widths from 1 to 8 bytes. The data-types are: - -* int8 -* int16 -* int32 -* int64 -* float32 -* float64 - -The number of primitives per data element is called the "arity" and can be any integer value greater than zero. For example UV might have an arity of 2, while position data -frequently has an arity of 3. - -## Encoding Strings - -While there is no explicit string type, one could encode string data by using a data-type uint8 with an arity of a fixed value (say 255) to store short strings. - -## Attribute Descriptor String - -Every attribute descriptor has a one to one mapping to a string representation similar to a URN: - - `g3d::::` - -This attribute descriptor string is the name of the buffer. - -# Recommended reading: - -* [VIM AEC blog post about using G3D with Unity](https://www.vimaec.com/the-g3d-geometry-exchange-format/) -* [Hackernoon article about BFast](https://hackernoon.com/bfast-a-data-format-for-serializing-named-binary-buffers-243p130uw) -* http://assimp.sourceforge.net/lib_html/structai_mesh.html -* http://help.autodesk.com/view/FBX/2017/ENU/?guid=__files_GUID_5EDC0280_E000_4B0B_88DF_5D215A589D5E_htm -* https://help.autodesk.com/cloudhelp/2017/ENU/Max-SDK/cpp_ref/class_mesh.html -* https://help.autodesk.com/view/3DSMAX/2016/ENU/?guid=__files_GUID_CBBA20AD_F7D5_46BC_9F5E_5EDA109F9CF4_htm -* http://paulbourke.net/dataformats/ -* http://paulbourke.net/dataformats/obj/ -* http://paulbourke.net/dataformats/ply/ -* http://paulbourke.net/dataformats/3ds/ -* https://github.com/KhronosGroup/gltf -* http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html diff --git a/src/cs/g3d/Vim.G3d/Validation.cs b/src/cs/g3d/Vim.G3d/Validation.cs deleted file mode 100644 index b1fde949..00000000 --- a/src/cs/g3d/Vim.G3d/Validation.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Collections.Generic; -using Vim.LinqArray; - -namespace Vim.G3d -{ - public enum G3dErrors - { - NodesCountMismatch, - MaterialsCountMismatch, - IndicesInvalidCount, - IndicesOutOfRange, - - //Submeshes - SubmeshesCountMismatch, - SubmeshesIndesxOffsetInvalidIndex, - SubmeshesIndexOffsetOutOfRange, - SubmeshesNonPositive, - SubmeshesMaterialOutOfRange, - - //Meshes - MeshesSubmeshOffsetOutOfRange, - MeshesSubmeshCountNonPositive, - - // Instances - InstancesCountMismatch, - InstancesParentOutOfRange, - InstancesMeshOutOfRange, - } - - public static class Validation - { - public static IEnumerable Validate(G3D g3d) - { - var errors = new List(); - - void Validate(bool value, G3dErrors error) - { - if (!value) errors.Add(error); - } - - //Indices - Validate(g3d.Indices.Count % 3 == 0, G3dErrors.IndicesInvalidCount); - Validate(g3d.Indices.All(i => i >= 0 && i < g3d.NumVertices), G3dErrors.IndicesOutOfRange); - //Triangle should have 3 distinct vertices - //Assert.That(g3d.Indices.SubArrays(3).Select(face => face.ToEnumerable().Distinct().Count()).All(c => c == 3)); - - //Submeshes - Validate(g3d.NumSubmeshes >= g3d.NumMeshes, G3dErrors.SubmeshesCountMismatch); - Validate(g3d.NumSubmeshes == g3d.SubmeshMaterials.Count, G3dErrors.SubmeshesCountMismatch); - Validate(g3d.NumSubmeshes == g3d.SubmeshIndexOffsets.Count, G3dErrors.SubmeshesCountMismatch); - Validate(g3d.SubmeshIndexOffsets.All(i => i % 3 == 0), G3dErrors.SubmeshesIndesxOffsetInvalidIndex); - Validate(g3d.SubmeshIndexOffsets.All(i => i >= 0 && i < g3d.NumCorners), G3dErrors.SubmeshesIndexOffsetOutOfRange); - Validate(g3d.SubmeshIndexCount.All(i => i > 0), G3dErrors.SubmeshesNonPositive); - Validate(g3d.SubmeshMaterials.All(m => m < g3d.NumMaterials), G3dErrors.SubmeshesMaterialOutOfRange); - - //Mesh - Validate(g3d.MeshSubmeshOffset.All(i => i >= 0 && i < g3d.NumSubmeshes), G3dErrors.MeshesSubmeshOffsetOutOfRange); - Validate(g3d.MeshSubmeshCount.All(i => i > 0), G3dErrors.MeshesSubmeshCountNonPositive); - - //Instances - Validate(g3d.NumInstances == g3d.InstanceParents.Count, G3dErrors.InstancesCountMismatch); - Validate(g3d.NumInstances == g3d.InstanceMeshes.Count, G3dErrors.InstancesCountMismatch); - Validate(g3d.NumInstances == g3d.InstanceTransforms.Count, G3dErrors.InstancesCountMismatch); - Validate(g3d.NumInstances == g3d.InstanceFlags.Count, G3dErrors.InstancesCountMismatch); - Validate(g3d.InstanceParents.All(i => i < g3d.NumInstances), G3dErrors.InstancesParentOutOfRange); - Validate(g3d.InstanceMeshes.All(i => i < g3d.NumMeshes), G3dErrors.InstancesMeshOutOfRange); - - //Materials - Validate(g3d.NumMaterials == g3d.MaterialColors.Count, G3dErrors.MaterialsCountMismatch); - Validate(g3d.NumMaterials == g3d.MaterialGlossiness.Count, G3dErrors.MaterialsCountMismatch); - Validate(g3d.NumMaterials == g3d.MaterialSmoothness.Count, G3dErrors.MaterialsCountMismatch); - - return errors; - } - } -} diff --git a/src/cs/g3d/Vim.G3d/Vim.G3d.csproj b/src/cs/g3d/Vim.G3d/Vim.G3d.csproj index 7f4cdcb4..b0086596 100644 --- a/src/cs/g3d/Vim.G3d/Vim.G3d.csproj +++ b/src/cs/g3d/Vim.G3d/Vim.G3d.csproj @@ -1,56 +1,22 @@  - - netstandard2.0 - true - G3D is a simple, efficient, generic binary format for storing and transmitting geometry. The G3D format is designed to be used either as a serialization format or as an in-memory data structure. G3D can represent triangular meshes, quadrilateral meshes, polygonal meshes, point clouds, and line segments. -It can be easily and efficiently deserialized and rendered in different languages and on different platforms. - true - true - snupkg - - - - true - - - - true + netstandard2.0 - - - True - - - + - + + - - - - CommonAttributes.tt - True - True - - - - - CommonAttributes.cs - TextTemplatingFileGenerator + + True + - - - - - - diff --git a/src/cs/math3d/Vim.Math3D.Tests/Util.cs b/src/cs/math3d/Vim.Math3D.Tests/Util.cs index f5b5b705..9878d302 100644 --- a/src/cs/math3d/Vim.Math3D.Tests/Util.cs +++ b/src/cs/math3d/Vim.Math3D.Tests/Util.cs @@ -99,7 +99,7 @@ public static T Abs(T value) where T : struct return value; } - dynamic dyn = (dynamic)value; + var dyn = (dynamic)value; var abs = Math.Abs(dyn); var ret = (T)abs; return ret; diff --git a/src/cs/samples/Vim.Gltf.Converter/VimToGltfStore.cs b/src/cs/samples/Vim.Gltf.Converter/VimToGltfStore.cs index b9882039..0df07ce4 100644 --- a/src/cs/samples/Vim.Gltf.Converter/VimToGltfStore.cs +++ b/src/cs/samples/Vim.Gltf.Converter/VimToGltfStore.cs @@ -3,9 +3,11 @@ using System.Diagnostics; using System.Linq; using System.Numerics; -using Vim.BFast; +using Vim.BFastLib; +using Vim.Format.Geometry; using Vim.G3d; -using Vim.LinqArray; +using Vim.Util; +using static Vim.Format.DocumentBuilder; namespace Vim.Gltf.Converter { @@ -26,7 +28,7 @@ public static void Convert(string vimFilePath, string gltfFilePath, float scale var gltfModel = ModelRoot.CreateModel(); var gltfScene = gltfModel.UseScene(0); - foreach (var vimMaterial in vim.Materials.ToEnumerable()) + foreach (var vimMaterial in vim.Materials) { var gltfMaterial = gltfModel.CreateMaterial(); gltfMaterial.InitializePBRMetallicRoughness(); @@ -47,22 +49,23 @@ public static void Convert(string vimFilePath, string gltfFilePath, float scale } } - var g3d = vim._SerializableDocument.Geometry; + var g3d = vim._SerializableDocument.GeometryNext; + var mesh = VimMesh.FromG3d(g3d); // Initialize a flat vertex buffer. - var vimVertexBufferBytes = g3d.Vertices.ToArray().SelectMany(v => new [] { v.X, v.Y, v.Z }).ToArray().ToBytes(); + var vimVertexBufferBytes = mesh.vertices.ToArray().SelectMany(v => new [] { v.X, v.Y, v.Z }).ToArray().ToBytes(); var gltfVertexBufferView = gltfModel.UseBufferView(vimVertexBufferBytes); var gltfVertexAccessor = gltfModel.CreateAccessor("VERTEX_ACCESSOR"); - gltfVertexAccessor.SetVertexData(gltfVertexBufferView, 0, g3d.Vertices.Count); + gltfVertexAccessor.SetVertexData(gltfVertexBufferView, 0, mesh.vertices.Length); // Initialize a flat index buffer. - var vimIndexBufferBytes = g3d.Indices.ToEnumerable().Select(i => (uint)i).ToArray().ToBytes(); + var vimIndexBufferBytes = mesh.indices.Select(i => (uint)i).ToArray().ToBytes(); var gltfIndexBufferView = gltfModel.UseBufferView(vimIndexBufferBytes); // Create the meshes and their primitives (submeshes) - var meshSubmeshCounts = g3d.MeshSubmeshCount.ToArray(); - var meshSubmeshOffsets = g3d.MeshSubmeshOffset.ToArray(); - for (var meshIndex = 0; meshIndex < meshSubmeshCounts.Length; ++meshIndex) + var meshSubmeshCounts = mesh.SubmeshCount.Select(m => g3d.GetMeshSubmeshCount(m)); + var meshSubmeshOffsets = mesh.SubmeshCount.Select(m => g3d.GetMeshSubmeshStart(m)); + for (var meshIndex = 0; meshIndex < meshSubmeshCounts.Count; ++meshIndex) { var meshSubmeshCount = meshSubmeshCounts[meshIndex]; if (meshSubmeshCount <= 0) @@ -74,9 +77,9 @@ public static void Convert(string vimFilePath, string gltfFilePath, float scale for (var submeshIndex = meshSubmeshOffset; submeshIndex < meshSubmeshOffset + meshSubmeshCount; ++submeshIndex) { - var submeshIndexOffset = g3d.SubmeshIndexOffsets[submeshIndex]; - var submeshIndexCount = g3d.SubmeshIndexCount[submeshIndex]; - var submeshMaterialIndex = g3d.SubmeshMaterials[submeshIndex]; + var submeshIndexOffset = mesh.submeshIndexOffsets[submeshIndex]; + var submeshIndexCount = mesh.submeshIndexCounts[submeshIndex]; + var submeshMaterialIndex = mesh.submeshMaterials[submeshIndex]; var gltfPrimitive = gltfMesh.CreatePrimitive(); gltfPrimitive.SetVertexAccessor("POSITION", gltfVertexAccessor); diff --git a/src/cs/samples/Vim.JsonDigest/AreaInfo.cs b/src/cs/samples/Vim.JsonDigest/AreaInfo.cs index 017f2c04..73e5f840 100644 --- a/src/cs/samples/Vim.JsonDigest/AreaInfo.cs +++ b/src/cs/samples/Vim.JsonDigest/AreaInfo.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; using System.Collections.Generic; -using Vim.LinqArray; +using System.Linq; namespace Vim.JsonDigest { @@ -71,6 +71,6 @@ public static IEnumerable GetAreaInfoCollection(VimScene vimScene) Area = a.Value, Perimeter = a.Perimeter, IsGrossInterior = a.IsGrossInterior - }).ToEnumerable(); + }); } } diff --git a/src/cs/samples/Vim.JsonDigest/MaterialInfo.cs b/src/cs/samples/Vim.JsonDigest/MaterialInfo.cs index 898d2e0a..9ad7e26f 100644 --- a/src/cs/samples/Vim.JsonDigest/MaterialInfo.cs +++ b/src/cs/samples/Vim.JsonDigest/MaterialInfo.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using Vim.LinqArray; namespace Vim.JsonDigest { @@ -70,7 +69,7 @@ public static IEnumerable GetMaterialInfoCollection(VimScene vimSc }).ToArray(); // Next, we iterate over all the MaterialInElement associative objects and update the MaterialInfos we created above. - foreach (var materialInElement in vimScene.DocumentModel.MaterialInElementList.ToEnumerable()) + foreach (var materialInElement in vimScene.DocumentModel.MaterialInElementList) { var material = materialInElement.Material; var element = materialInElement.Element; diff --git a/src/cs/samples/Vim.JsonDigest/RoomInfo.cs b/src/cs/samples/Vim.JsonDigest/RoomInfo.cs index 005f53d0..2759c608 100644 --- a/src/cs/samples/Vim.JsonDigest/RoomInfo.cs +++ b/src/cs/samples/Vim.JsonDigest/RoomInfo.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using Vim.LinqArray; +using System.Linq; namespace Vim.JsonDigest { @@ -63,6 +63,6 @@ public static IEnumerable GetRoomInfoCollection(VimScene vimScene) Area = r.Area, Volume = r.Volume, Perimeter = r.Perimeter - }).ToEnumerable(); + }); } } diff --git a/src/cs/util/Vim.Util/ArrayExtensions.cs b/src/cs/util/Vim.Util/ArrayExtensions.cs new file mode 100644 index 00000000..2adf4a2f --- /dev/null +++ b/src/cs/util/Vim.Util/ArrayExtensions.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Vim.Util +{ + public static class ArrayExtensions + { + public static IList Select(this int count, Func f) + { + var array = new T[count]; + + for (var i = 0; i < count; i++) + { + array[i] = f(i); + } + + return array; + } + + public static IList Repeat(this T value, int count) + { + var array = new T[count]; + + for (var i = 0; i < count; i++) + { + array[i] = value; + } + + return array; + } + + public static IList SelectByIndex(this IList self, IList indices) + => indices.Select(i => self[i]).ToArray(); + + public static IEnumerable Indices(this IList self) + => Enumerable.Range(0, self.Count).ToArray(); + + public static IList Slice(this IList self, int from, int to) + => Select(to - from, i => self[i + from]); + + public static IList SubArray(this IList self, int from, int count) + => self.Slice(from, count + from); + + public static IList Range(this int self) + => Select(self, i => i); + + /// + /// Creates a new array that concatenates a unit item list of one item before it + /// Repeatedly calling Prepend would result in significant performance degradation. + /// + public static IList Prepend(this IList self, T x) + => (self.Count + 1).Select(i => i == 0 ? x : self[i - 1]); + + /// + /// Applies a function (like "+") to each element in the series to create an effect similar to partial sums. + /// The first value in the array will be zero. + /// + public static IList PostAccumulate(this IList self, Func f, T init = default) + { + var n = self.Count; + var r = new T[n + 1]; + var prev = r[0] = init; + if (n == 0) return r; + for (var i = 0; i < n; ++i) + { + prev = r[i + 1] = f(prev, self[i]); + } + return r; + } + + /// + /// Returns a new array containing all elements excluding the last n elements. + /// + public static IList DropLast(this IList self, int n = 1) + => self.Count > n ? self.Take(self.Count - n).ToArray() : Array.Empty(); + + public static int IndexOf(this long[] self, long value) + { + for (var i = 0; i < self.Length; i++) + { + if (self[i] == value) + { + return i; + } + } + + return -1; + } + } +} diff --git a/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs b/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs index c176413c..4b0ba549 100644 --- a/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs +++ b/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs @@ -83,7 +83,7 @@ private static CodeBuilder WriteDocumentEntityData(Type t, CodeBuilder cb, Entit var dataColumnGetter = $"{t.Name}EntityTable?.{functionName}(\"{eci.SerializedValueColumnName}\")"; if (eci.EntityColumnAttribute.SerializedType != fieldType) { - dataColumnGetter += $"?.Select(v => ({fieldTypeName}) v)"; + dataColumnGetter += $"?.Select(v => ({fieldTypeName}) v).ToArray()"; } return dataColumnGetter; }).ToArray(); @@ -92,9 +92,9 @@ private static CodeBuilder WriteDocumentEntityData(Type t, CodeBuilder cb, Entit ? $"({string.Join(" ?? ", dataColumnGetters)})" : dataColumnGetters[0]; - cb.AppendLine($"public IArray<{fieldTypeName}> {t.Name}{fieldName} {{ get; }}"); + cb.AppendLine($"public {fieldTypeName}[] {t.Name}{fieldName} {{ get; }}"); constructor.ArraysInitializers - .Add($"{t.Name}{fieldName} = {dataColumnGetterString} ?? Array.Empty<{fieldTypeName}>().ToIArray();"); + .Add($"{t.Name}{fieldName} = {dataColumnGetterString} ?? Array.Empty<{fieldTypeName}>();"); // Safe accessor. var defaultValue = baseStrategy == ValueSerializationStrategy.SerializeAsStringColumn ? "\"\"" : "default"; @@ -106,9 +106,9 @@ private static CodeBuilder WriteDocumentEntityData(Type t, CodeBuilder cb, Entit { var (indexColumnName, localFieldName) = fieldInfo.GetIndexColumnInfo(); - cb.AppendLine($"public IArray {t.Name}{localFieldName}Index {{ get; }}"); + cb.AppendLine($"public int[] {t.Name}{localFieldName}Index {{ get; }}"); constructor.RelationalColumns - .Add($"{t.Name}{localFieldName}Index = {t.Name}EntityTable?.GetIndexColumnValues(\"{indexColumnName}\") ?? Array.Empty().ToIArray();"); + .Add($"{t.Name}{localFieldName}Index = {t.Name}EntityTable?.GetIndexColumnValues(\"{indexColumnName}\") ?? Array.Empty();"); cb.AppendLine($"public int Get{t.Name}{localFieldName}Index(int index) => {t.Name}{localFieldName}Index?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None;"); } @@ -117,7 +117,7 @@ private static CodeBuilder WriteDocumentEntityData(Type t, CodeBuilder cb, Entit cb.AppendLine($"public int Num{t.Name} => {t.Name}EntityTable?.NumRows ?? 0;"); // Entity lists - cb.AppendLine($"public IArray<{t.Name}> {t.Name}List {{ get; }}"); + cb.AppendLine($"public {t.Name}[] {t.Name}List {{ get; }}"); // Element getter function cb.AppendLine($"public {t.Name} Get{t.Name}(int n)"); @@ -222,8 +222,10 @@ IEnumerable GetEquatableFields(FieldInfo[] fis) return cb; } - private static CodeBuilder WriteDocument(CodeBuilder cb) + private static CodeBuilder WriteDocument(CodeBuilder cb = null) { + cb = cb ?? new CodeBuilder(); + var entityTypes = ObjectModelReflection.GetEntityTypes().ToArray(); foreach (var et in entityTypes) @@ -241,7 +243,7 @@ private static CodeBuilder WriteDocument(CodeBuilder cb) cb.AppendLine("// All entity collections"); cb.AppendLine("public Dictionary> AllEntities => new Dictionary>() {"); foreach (var t in entityTypes) - cb.AppendLine($"{{\"{t.GetEntityTableName()}\", {t.Name}List.ToEnumerable()}},"); + cb.AppendLine($"{{\"{t.GetEntityTableName()}\", {t.Name}List}},"); cb.AppendLine("};"); cb.AppendLine(); @@ -275,7 +277,7 @@ private static CodeBuilder WriteDocument(CodeBuilder cb) cb.AppendLine("// Initialize entity collections"); foreach (var t in entityTypes) - cb.AppendLine($"{t.Name}List = Num{t.Name}.Select(i => Get{t.Name}(i));"); + cb.AppendLine($"{t.Name}List = Enumerable.Range(0, Num{t.Name}).Select(i => Get{t.Name}(i)).ToArray();"); cb.AppendLine(); cb.AppendLine("// Initialize element index maps"); @@ -506,8 +508,6 @@ public static void WriteDocument(string file) cb.AppendLine("using System.Collections.Generic;"); cb.AppendLine("using System.Linq;"); cb.AppendLine("using Vim.Math3d;"); - cb.AppendLine("using Vim.LinqArray;"); - cb.AppendLine("using Vim.Format.ObjectModel;"); cb.AppendLine("using Vim.Util;"); cb.AppendLine(); diff --git a/src/cs/vim/Vim.Format.CodeGen/ObjectModelTypeScriptGenerator.cs b/src/cs/vim/Vim.Format.CodeGen/ObjectModelTypeScriptGenerator.cs index 70e40bdc..b665b612 100644 --- a/src/cs/vim/Vim.Format.CodeGen/ObjectModelTypeScriptGenerator.cs +++ b/src/cs/vim/Vim.Format.CodeGen/ObjectModelTypeScriptGenerator.cs @@ -405,8 +405,8 @@ private static void WriteVimDocument(CodeBuilder cb, Type[] entityTypes) cb.AppendLine("}"); cb.AppendLine(); - cb.AppendLine("static async createFromBfast(bfast: BFast, ignoreStrings: boolean = false): Promise {"); - cb.AppendLine("const loaded = await VimLoader.loadFromBfast(bfast, ignoreStrings)"); + cb.AppendLine("static async createFromBfast(bfast: BFast, download:boolean, ignoreStrings: boolean = false): Promise {"); + cb.AppendLine("const loaded = await VimLoader.loadFromBfast(bfast, download, ignoreStrings)"); cb.AppendLine(); cb.AppendLine("if (loaded[0] === undefined)"); cb.AppendLine(" return undefined"); diff --git a/src/cs/vim/Vim.Format.CodeGen/Vim.Format.CodeGen.csproj b/src/cs/vim/Vim.Format.CodeGen/Vim.Format.CodeGen.csproj index ec673b10..89a08ae0 100644 --- a/src/cs/vim/Vim.Format.CodeGen/Vim.Format.CodeGen.csproj +++ b/src/cs/vim/Vim.Format.CodeGen/Vim.Format.CodeGen.csproj @@ -8,7 +8,7 @@ OnOutputUpdated - + @@ -16,12 +16,18 @@ - - + + + + + True + + + diff --git a/src/cs/vim/Vim.Format.Core/AssetInfo.cs b/src/cs/vim/Vim.Format.Core/AssetInfo.cs index 303c5d27..b790e097 100644 --- a/src/cs/vim/Vim.Format.Core/AssetInfo.cs +++ b/src/cs/vim/Vim.Format.Core/AssetInfo.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.IO; -using Vim.BFast; using Vim.Util; -using Vim.LinqArray; +using Vim.BFastLib; +using System.Linq; namespace Vim.Format { @@ -18,7 +18,7 @@ public enum AssetType /// public class AssetInfo { - public const char Separator = '/'; + private const char Separator = '/'; public readonly string Name; @@ -27,7 +27,7 @@ public class AssetInfo public AssetInfo(string name, AssetType assetType) => (Name, AssetType) = (name, assetType); - public static string[] SplitAssetBufferName(string assetBufferName) + private static string[] SplitAssetBufferName(string assetBufferName) => assetBufferName?.Split(Separator); public static AssetInfo Parse(string assetBufferName) @@ -63,10 +63,10 @@ public static bool TryParse(string assetBufferName, out AssetInfo assetInfo) } } - public static string AssetTypeToString(AssetType assetType) + private static string AssetTypeToString(AssetType assetType) => assetType.ToString("G").ToLowerInvariant(); - public string AssetTypeString + private string AssetTypeString => AssetTypeToString(AssetType); public override string ToString() @@ -81,19 +81,13 @@ public string GetDefaultAssetFilePathInDirectory(DirectoryInfo directoryInfo) public static class AssetInfoExtensions { - public static bool IsTexture(this INamedBuffer buffer) - => AssetInfo.TryParse(buffer.Name, out var assetInfo) && assetInfo.AssetType == AssetType.Texture; - - public static INamedBuffer GetAssetBuffer(this Document doc, string assetBufferName) + private static INamedBuffer GetAssetBuffer(this Document doc, string assetBufferName) => doc.Assets.GetOrDefault(assetBufferName); - public static IEnumerable GetTextures(this Document d) - => d.Assets.Values.Where(IsTexture); - /// /// Extracts the asset buffer to the file designated by the given FileInfo. /// - public static FileInfo ExtractAsset(this INamedBuffer assetBuffer, FileInfo fileInfo) + private static FileInfo ExtractAsset(this INamedBuffer assetBuffer, FileInfo fileInfo) { Util.IO.CreateFileDirectory(fileInfo.FullName); using (var stream = fileInfo.Create()) @@ -105,7 +99,7 @@ public static FileInfo ExtractAsset(this INamedBuffer assetBuffer, FileInfo file /// Extracts the asset and returns a FileInfo representing the extracted asset on disk.
/// Returns null if the asset could not be extracted. ///
- public static FileInfo ExtractAsset(this INamedBuffer assetBuffer, DirectoryInfo directoryInfo) + private static FileInfo ExtractAsset(this INamedBuffer assetBuffer, DirectoryInfo directoryInfo) => !AssetInfo.TryParse(assetBuffer.Name, out var assetInfo) ? null : assetBuffer.ExtractAsset(new FileInfo(assetInfo.GetDefaultAssetFilePathInDirectory(directoryInfo))); @@ -123,7 +117,7 @@ public static FileInfo ExtractAsset(this Document doc, string assetBufferName, F public static IEnumerable<(string assetBufferName, FileInfo assetFileInfo)> ExtractAssets(this Document doc, DirectoryInfo directoryInfo) { var result = new List<(string assetBufferName, FileInfo assetFileInfo)>(); - foreach (var assetBuffer in doc.Assets.Values.ToEnumerable()) + foreach (var assetBuffer in doc.Assets.Values) { var assetBufferName = assetBuffer.Name; var assetFilePath = assetBuffer.ExtractAsset(directoryInfo); @@ -135,7 +129,7 @@ public static FileInfo ExtractAsset(this Document doc, string assetBufferName, F /// /// Gets the byte array which defines the given asset. Returns false if the asset was not found or if the byte array is empty or null. /// - public static bool TryGetAssetBytes(this Document doc, string assetBufferName, out byte[] bytes) + private static bool TryGetAssetBytes(this Document doc, string assetBufferName, out byte[] bytes) { bytes = null; @@ -151,7 +145,7 @@ public static bool TryGetAssetBytes(this Document doc, string assetBufferName, o /// /// Gets the byte array which defines the given asset. Returns false if the asset was not found or if the byte array is empty or null. /// - public static bool TryGetAssetBytes(this Document doc, AssetType assetType, string assetName, out byte[] bytes) + private static bool TryGetAssetBytes(this Document doc, AssetType assetType, string assetName, out byte[] bytes) => doc.TryGetAssetBytes(new AssetInfo(assetName, assetType).ToString(), out bytes); /// diff --git a/src/cs/vim/Vim.Format.Core/BigG3dWriter.cs b/src/cs/vim/Vim.Format.Core/BigG3dWriter.cs deleted file mode 100644 index e70ac297..00000000 --- a/src/cs/vim/Vim.Format.Core/BigG3dWriter.cs +++ /dev/null @@ -1,210 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Vim.G3d; -using Vim.BFast; -using System.IO; -using static Vim.Format.DocumentBuilder; - -namespace Vim.Format -{ - /// - /// This is a helper class for writing the really big G3Ds needed in a VIM - /// - public class BigG3dWriter : IBFastComponent - { - public INamedBuffer Meta { get; } - public string[] Names { get; } - public long[] Sizes { get; } - public BFastHeader Header { get; } - public List Meshes { get; } - public List Instances { get; } - public List Shapes { get; } - public List Materials { get; } - - // Computed fields - public int[] MeshVertexOffsets { get; } - public int[] MeshIndexOffsets { get; } - public int[] MeshSubmeshOffset { get; } - public int[] SubmeshIndexOffsets { get; } - public int[] ShapeVertexOffsets { get; } - - public BigG3dWriter(List meshes, List instances, List shapes, List materials, G3dHeader? header = null, bool useColors = false) - { - Meshes = meshes; - Instances = instances; - Shapes = shapes; - Materials = materials; - var totalSubmeshCount = meshes.Select(s => s.SubmeshesIndexOffset.Count).Sum(); - - // Compute the Vertex offsets and index offsets - MeshVertexOffsets = new int[meshes.Count]; - MeshIndexOffsets = new int[meshes.Count]; - SubmeshIndexOffsets = new int[totalSubmeshCount]; - MeshSubmeshOffset = new int[meshes.Count]; - - var n = meshes.Count; - - for (var i = 1; i < n; ++i) - { - MeshVertexOffsets[i] = MeshVertexOffsets[i - 1] + meshes[i - 1].Vertices.Count; - MeshIndexOffsets[i] = MeshIndexOffsets[i - 1] + meshes[i - 1].Indices.Count; - MeshSubmeshOffset[i] = MeshSubmeshOffset[i - 1] + meshes[i - 1].SubmeshesIndexOffset.Count; - } - - var subIndex =0; - var previousIndexCount = 0; - foreach(var geo in meshes) - { - foreach(var sub in geo.SubmeshesIndexOffset) - { - SubmeshIndexOffsets[subIndex++] = sub + previousIndexCount; - } - previousIndexCount += geo.Indices.Count; - } - - var submeshCount = meshes.Select(s => s.SubmeshesIndexOffset.Count).Sum(); - - var totalVertices = n == 0 ? 0 : MeshVertexOffsets[n - 1] + meshes[n - 1].Vertices.Count; - var totalIndices = n == 0 ? 0 : MeshIndexOffsets[n - 1] + meshes[n - 1].Indices.Count; - long totalFaces = totalIndices / 3; - - // Compute the shape vertex offsets - var numShapes = shapes.Count; - ShapeVertexOffsets = new int[numShapes]; - for (var i = 1; i < numShapes; ++i) - { - ShapeVertexOffsets[i] = ShapeVertexOffsets[i - 1] + shapes[i - 1].Vertices.Count; - } - var numShapeVertices = numShapes == 0 ? 0 : ShapeVertexOffsets[numShapes - 1] + shapes[numShapes - 1].Vertices.Count; - - Meta = (header ?? G3dHeader.Default).ToBytes().ToNamedBuffer("meta"); - - (long size, string name) AttributeSizeAndName(string attributeName, long count) - => (AttributeDescriptor.Parse(attributeName).DataElementSize * count, attributeName); - - var writers = new List<(long size, string attribute)>() - { - (Meta.NumBytes(), Meta.Name), - AttributeSizeAndName(CommonAttributes.Position, totalVertices), - AttributeSizeAndName(CommonAttributes.Index, totalIndices), - - AttributeSizeAndName(CommonAttributes.MeshSubmeshOffset, meshes.Count), - AttributeSizeAndName(CommonAttributes.SubmeshIndexOffset, submeshCount), - AttributeSizeAndName(CommonAttributes.SubmeshMaterial, submeshCount), - - AttributeSizeAndName(CommonAttributes.InstanceTransform, instances.Count), - AttributeSizeAndName(CommonAttributes.InstanceParent, instances.Count), - AttributeSizeAndName(CommonAttributes.InstanceMesh, instances.Count), - AttributeSizeAndName(CommonAttributes.InstanceFlags, instances.Count), - - AttributeSizeAndName(CommonAttributes.ShapeVertex, numShapeVertices), - AttributeSizeAndName(CommonAttributes.ShapeVertexOffset, numShapes), - AttributeSizeAndName(CommonAttributes.ShapeColor, numShapes), - AttributeSizeAndName(CommonAttributes.ShapeWidth, numShapes), - - AttributeSizeAndName(CommonAttributes.MaterialColor, materials.Count), - AttributeSizeAndName(CommonAttributes.MaterialGlossiness, materials.Count), - AttributeSizeAndName(CommonAttributes.MaterialSmoothness, materials.Count), - }; - - if (useColors) - { - writers.Add(AttributeSizeAndName(CommonAttributes.VertexColor, totalVertices)); - } - - Names = writers.Select(w => w.attribute).ToArray(); - Sizes = writers.Select(w => w.size).ToArray(); - Header = BFast.BFast.CreateBFastHeader(Sizes, Names); - } - - public long GetSize() - => BFast.BFast.ComputeNextAlignment(Header.Preamble.DataEnd); - - public void Write(Stream stream) - { - // TODO: validate in debug mode that this is producing the current data model. Look at the schema! - - stream.WriteBFastHeader(Header); - stream.WriteBFastBody(Header, Names, Sizes, (_stream, index, name, size) => - { - switch (name) - { - case "meta": - _stream.Write(Meta); - break; - - // Vertices - case CommonAttributes.Position: - Meshes.ForEach(g => stream.Write(g.Vertices.ToArray())); - break; - - // Indices - case CommonAttributes.Index: - for (var i = 0; i < Meshes.Count; ++i) - { - var g = Meshes[i]; - var offset = MeshVertexOffsets[i]; - stream.Write(g.Indices.Select(idx => idx + offset).ToArray()); - } - break; - - // Meshes - case CommonAttributes.MeshSubmeshOffset: - stream.Write(MeshSubmeshOffset); - break; - - // Instances - case CommonAttributes.InstanceMesh: - stream.Write(Instances.Select(i => i.MeshIndex).ToArray()); - break; - case CommonAttributes.InstanceTransform: - stream.Write(Instances.Select(i => i.Transform).ToArray()); - break; - case CommonAttributes.InstanceParent: - stream.Write(Instances.Select(i => i.ParentIndex).ToArray()); - break; - case CommonAttributes.InstanceFlags: - stream.Write(Instances.Select(i => (ushort) i.InstanceFlags).ToArray()); - break; - - // Shapes - case CommonAttributes.ShapeVertex: - stream.Write(Shapes.SelectMany(s => s.Vertices).ToArray()); - break; - case CommonAttributes.ShapeVertexOffset: - stream.Write(ShapeVertexOffsets); - break; - case CommonAttributes.ShapeColor: - stream.Write(Shapes.Select(s => s.Color).ToArray()); - break; - case CommonAttributes.ShapeWidth: - stream.Write(Shapes.Select(s => s.Width).ToArray()); - break; - - // Materials - case CommonAttributes.MaterialColor: - stream.Write(Materials.Select(i => i.Color).ToArray()); - break; - case CommonAttributes.MaterialGlossiness: - stream.Write(Materials.Select(i => i.Glossiness).ToArray()); - break; - case CommonAttributes.MaterialSmoothness: - stream.Write(Materials.Select(i => i.Smoothness).ToArray()); - break; - - // Submeshes - case CommonAttributes.SubmeshIndexOffset: - stream.Write(SubmeshIndexOffsets); - break; - case CommonAttributes.SubmeshMaterial: - stream.Write(Meshes.SelectMany(s => s.SubmeshMaterials).ToArray()); - break; - default: - throw new Exception($"Not a recognized geometry buffer: {name}"); - } - return size; - }); - } - } -} diff --git a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs index 3cce6b9f..0a8adf85 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs @@ -2,17 +2,14 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using Vim.BFast; -using Vim.LinqArray; +using Vim.BFastLib; +using Vim.Util; namespace Vim.Format { public static partial class ColumnExtensions { - public static INamedBuffer[] GetAllColumns(this SerializableEntityTable et) - => et.DataColumns.Concat(et.IndexColumns).Concat(et.StringColumns).ToArray(); - - public static INamedBuffer[] ValidateColumnRowsAreAligned(this INamedBuffer[] columns) + public static void ValidateColumnRowsAreAligned(this IEnumerable columns) { var numRows = columns.FirstOrDefault()?.NumElements() ?? 0; @@ -25,27 +22,12 @@ public static INamedBuffer[] ValidateColumnRowsAreAligned(this INamedBuffer[] co var msg = $"Column '{column.Name}' has {columnRows} rows which does not match the first column's {numRows} rows"; Debug.Fail(msg); } - - return columns; } - public static INamedBuffer[] ValidateColumnRowsAreAligned(this SerializableEntityTable et) - => et.GetAllColumns().ValidateColumnRowsAreAligned(); - - public static string ValidateCanConcatBuffers(this INamedBuffer thisBuffer, INamedBuffer otherBuffer) + public static SerializableEntityTable ValidateColumnRowsAreAligned(this SerializableEntityTable et) { - var thisPrefix = thisBuffer.GetTypePrefix(); - if (string.IsNullOrEmpty(thisPrefix)) - throw new Exception("NamedBuffer prefix not found"); - - var otherPrefix = otherBuffer.GetTypePrefix(); - if (string.IsNullOrEmpty(otherPrefix)) - throw new Exception("NamedBuffer prefix not found"); - - if (thisPrefix != otherPrefix) - throw new Exception("NamedBuffer prefixes are not equal"); - - return thisPrefix; + et.AllColumns.ValidateColumnRowsAreAligned(); + return et; } public static T[] RemapData(this T[] self, List remapping = null) @@ -54,9 +36,9 @@ public static T[] RemapData(this T[] self, List remapping = null) public static IBuffer ToBuffer(this T[] array) where T : unmanaged => new Buffer(array); - public const string UnknownNamedBufferPrefix = "Unknown NamedBuffer prefix"; + private const string UnknownNamedBufferPrefix = "Unknown NamedBuffer prefix"; - public static object GetDataColumnValue(this IBuffer dataColumn, string typePrefix, int rowIndex) + private static object GetDataColumnValue(this IBuffer dataColumn, string typePrefix, int rowIndex) { switch (typePrefix) { @@ -76,7 +58,10 @@ public static object GetDataColumnValue(this IBuffer dataColumn, string typePref } public static object GetDataColumnValue(this INamedBuffer dataColumn, int rowIndex) - => dataColumn.GetDataColumnValue(dataColumn.GetTypePrefix(), rowIndex); + { + var prefix = SerializableEntityTable.GetTypeFromName(dataColumn.Name); + return dataColumn.GetDataColumnValue(prefix, rowIndex); + } public static IBuffer CreateDefaultDataColumnBuffer(int length, string typePrefix) { @@ -118,11 +103,11 @@ public static IBuffer CopyDataColumn(this IBuffer dataColumn, string typePrefix, public static INamedBuffer CopyDataColumn(this INamedBuffer dataColumn, List remapping = null) { - var typePrefix = dataColumn.GetTypePrefix(); + var typePrefix = SerializableEntityTable.GetTypeFromName(dataColumn.Name); return new NamedBuffer(dataColumn.CopyDataColumn(typePrefix, remapping), dataColumn.Name); } - public static IBuffer Concat(this IBuffer thisBuffer, IBuffer otherBuffer) where T : unmanaged + private static IBuffer Concat(this IBuffer thisBuffer, IBuffer otherBuffer) where T : unmanaged => thisBuffer.AsArray().Concat(otherBuffer.AsArray()).ToArray().ToBuffer(); public static IBuffer ConcatDataColumnBuffers(this IBuffer thisBuffer, IBuffer otherBuffer, string typePrefix) @@ -144,67 +129,13 @@ public static IBuffer ConcatDataColumnBuffers(this IBuffer thisBuffer, IBuffer o } } - public static INamedBuffer ConcatDataColumns(this INamedBuffer thisColumn, INamedBuffer otherColumn) - { - var typePrefix = thisColumn.ValidateCanConcatBuffers(otherColumn); - var combinedBuffer = thisColumn.ConcatDataColumnBuffers(otherColumn, typePrefix); - return new NamedBuffer(combinedBuffer, thisColumn.Name); - } - - public static List ConcatColumns( - this IReadOnlyList thisColumnList, - IReadOnlyList otherColumnList, - Func concatFunc) where T : INamedBuffer - { - var mergedColumns = new List(); - - foreach (var thisColumn in thisColumnList) - { - var otherColumn = otherColumnList.FirstOrDefault(c => c.Name == thisColumn.Name); - if (otherColumn == null) - continue; - - var newNamedBuffer = concatFunc(thisColumn, otherColumn); - - mergedColumns.Add(newNamedBuffer); - } - - return mergedColumns; - } - - public static List ConcatDataColumns(this IReadOnlyList thisColumnList, IReadOnlyList otherColumnList) - => thisColumnList.ConcatColumns(otherColumnList, - (a, b) => a.ConcatDataColumns(b)); - - public static List> ConcatIntColumns(this IReadOnlyList> thisColumnList, IReadOnlyList> otherColumnList) - => thisColumnList.ConcatColumns(otherColumnList, - (a, b) => new NamedBuffer(a.GetTypedData().Concat(b.GetTypedData()).ToArray(), a.Name)); - - /// - /// Returns a concatenated SerializableEntityTable based on the column names of thisTable. - /// - public static SerializableEntityTable Concat( - this SerializableEntityTable thisTable, - SerializableEntityTable otherTable) - { - var concatenated = new SerializableEntityTable - { - Name = thisTable.Name, - IndexColumns = thisTable.IndexColumns.ConcatIntColumns(otherTable.IndexColumns), - StringColumns = thisTable.StringColumns.ConcatIntColumns(otherTable.StringColumns), - DataColumns = thisTable.DataColumns.ConcatDataColumns(otherTable.DataColumns), - }; - concatenated.ValidateColumnRowsAreAligned(); - return concatenated; - } - public static T[] GetColumnValues(this INamedBuffer nb) where T : unmanaged => nb.AsArray(); /// /// Returns a new collection of index columns in which the designated column names have repeated values of VimConstants.NoEntityRelation. /// - public static IEnumerable> NoneIndexColumnRelations( + private static IEnumerable> NoneIndexColumnRelations( this IEnumerable> indexColumns, params string[] indexColumnNames) => indexColumns.Select(ic => indexColumnNames.Contains(ic.Name) diff --git a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs index d7cedece..3fd3a49b 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs @@ -16,10 +16,10 @@ public static string GetDataColumnNameTypePrefix(this Type type) throw new Exception($"{nameof(GetDataColumnNameTypePrefix)} error: no matching data column name prefix for {type}"); } - public static bool CanSerializeAsStringColumn(this Type type) + private static bool CanSerializeAsStringColumn(this Type type) => type == typeof(string); - public static bool CanSerializeAsDataColumn(this Type type) + private static bool CanSerializeAsDataColumn(this Type type) => DataColumnTypes.Contains(type); public static ValueSerializationStrategy GetValueSerializationStrategy(this Type type) @@ -60,10 +60,7 @@ public static string GetSerializedValueColumnName(this FieldInfo fieldInfo) return $"{typePrefix}{fieldInfo.GetSerializedValueName()}"; } - public static string GetSerializedIndexName(this FieldInfo fieldInfo) - => fieldInfo.Name.Trim('_'); - - public static bool IsRelationType(this Type t) + private static bool IsRelationType(this Type t) => t.Name == "Relation`1"; public static Type RelationTypeParameter(this Type t) diff --git a/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs b/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs index 0d826c14..e2a5e1e8 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs @@ -7,7 +7,7 @@ namespace Vim.Format { public static partial class ColumnExtensions { - public static readonly IReadOnlyCollection AllColumnInfos + private static readonly IReadOnlyCollection AllColumnInfos = new[] { new ColumnInfo(ColumnType.IndexColumn, VimConstants.IndexColumnNameTypePrefix, typeof(int)), @@ -19,10 +19,7 @@ public static readonly IReadOnlyCollection AllColumnInfos new ColumnInfo(ColumnType.DataColumn, VimConstants.FloatColumnNameTypePrefix, typeof(float)), }; - public static readonly IReadOnlyDictionary TypePrefixToColumnTypeMap - = AllColumnInfos.ToDictionary(t => t.TypePrefix, t => t.ColumnType); - - public static readonly IReadOnlyDictionary DataColumnTypeToPrefixMap + private static readonly IReadOnlyDictionary DataColumnTypeToPrefixMap = AllColumnInfos .Where(t => t.ColumnType == ColumnType.DataColumn) .SelectMany(t => t.RelatedTypes.Select(type => (Type: type, t.TypePrefix))) @@ -31,27 +28,25 @@ public static readonly IReadOnlyDictionary DataColumnTypeToPrefixM public static readonly ISet DataColumnTypes = new HashSet(AllColumnInfos.Where(t => t.ColumnType == ColumnType.DataColumn).SelectMany(t => t.RelatedTypes)); - public static readonly ISet DataColumnNameTypePrefixes + private static readonly ISet DataColumnNameTypePrefixes = new HashSet(AllColumnInfos.Where(t => t.ColumnType == ColumnType.DataColumn).Select(t => t.TypePrefix)); - public static readonly Regex DataColumnTypePrefixRegex + private static readonly Regex DataColumnTypePrefixRegex = new Regex($@"^(?:{string.Join("|", DataColumnNameTypePrefixes)})"); - public static bool TryGetDataColumnNameTypePrefix(string columnName, out string typePrefix) + private static bool TryGetDataColumnNameTypePrefix(string columnName) { - typePrefix = null; if (string.IsNullOrEmpty(columnName)) return false; var match = DataColumnTypePrefixRegex.Match(columnName); - typePrefix = match.Value; return match.Success; } public static bool IsDataColumnName(string columnName) - => TryGetDataColumnNameTypePrefix(columnName, out _); + => TryGetDataColumnNameTypePrefix(columnName); - public const string RelatedTableNameFieldNameSeparator = ":"; + private const string RelatedTableNameFieldNameSeparator = ":"; public static string GetIndexColumnName(string relatedTableName, string localFieldName) => VimConstants.IndexColumnNameTypePrefix + relatedTableName + RelatedTableNameFieldNameSeparator + localFieldName; diff --git a/src/cs/vim/Vim.Format.Core/Document.cs b/src/cs/vim/Vim.Format.Core/Document.cs index 476bd8e7..38d627fc 100644 --- a/src/cs/vim/Vim.Format.Core/Document.cs +++ b/src/cs/vim/Vim.Format.Core/Document.cs @@ -1,5 +1,11 @@ -using Vim.LinqArray; -using Vim.BFast; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using Vim.BFastLib; +using Vim.Format; +using Vim.G3d; +using Vim.Util; namespace Vim.Format { @@ -10,21 +16,55 @@ public Document(SerializableDocument document) { _Document = document; Header = _Document.Header; - Geometry = _Document.Geometry; - StringTable = _Document.StringTable.ToIArray(); - EntityTables = _Document.EntityTables.ToLookup( + GeometryNext = _Document.GeometryNext; + StringTable = _Document.StringTable; + + EntityTables = _Document.EntityTables.ToDictionary( et => et.Name, - et => et.ToEntityTable(this)); - Assets = _Document.Assets.ToLookup(et => et.Name, et => et); + et => new EntityTable(this, et)); + + + + Assets = _Document.Assets.ToDictionary(et => et.Name, et => et); + + var tables = document.EntityTables.ToDictionary( + et => et.Name, + et => new EntityTable(this, et)); + _bim = new Bim(tables); } - public string FileName => _Document.FileName; + private readonly Bim _bim; + + public EntityTable GetTable(string name) + => _bim.GetTable(name); + + public IEnumerable TableNames => _bim.TableNames; + public IEnumerable Tables => _bim.Tables; + public VimSchema GetSchema() => VimSchema.Create(_Document); + private SerializableDocument _Document { get; } public SerializableHeader Header { get; } - public ILookup EntityTables { get; } - public ILookup Assets { get; } - public IArray StringTable { get; } + public Dictionary EntityTables { get; } + public Dictionary Assets { get; } + public string[] StringTable { get; } public string GetString(int index) => StringTable.ElementAtOrDefault(index); - public G3d.G3D Geometry { get; } + public G3dVim GeometryNext { get; } } } + +public class Bim +{ + private readonly Dictionary _tables; + + public Bim(Dictionary tables) + { + _tables = tables; + } + + public EntityTable GetTable(string name) + => _tables.GetOrDefault(name); + + public IEnumerable Tables => _tables.Values; + public IEnumerable TableNames => Tables.Select(e => e.Name); + +} diff --git a/src/cs/vim/Vim.Format.Core/DocumentBuilder.cs b/src/cs/vim/Vim.Format.Core/DocumentBuilder.cs index a1d54198..4a11616e 100644 --- a/src/cs/vim/Vim.Format.Core/DocumentBuilder.cs +++ b/src/cs/vim/Vim.Format.Core/DocumentBuilder.cs @@ -3,9 +3,10 @@ using System.Diagnostics; using System.Linq; using Vim.Math3d; -using Vim.BFast; +using Vim.BFastLib; using System.IO; using Vim.Util; +using Vim.Format.Geometry; namespace Vim.Format { @@ -14,12 +15,8 @@ public partial class DocumentBuilder public readonly SerializableHeader Header; public readonly Dictionary Tables = new Dictionary(); public readonly Dictionary Assets = new Dictionary(); - public readonly List Meshes = new List(); - public readonly List Instances = new List(); - public readonly List Shapes = new List(); - public readonly List Materials = new List(); + public readonly G3dBuilder Geometry = new G3dBuilder(); - public bool UseColors { get; set; } public DocumentBuilder( string generator, @@ -51,51 +48,64 @@ public DocumentBuilder AddAsset(string name, byte[] asset) return this; } - public DocumentBuilder AddMesh(SubdividedMesh g) + public DocumentBuilder AddMesh(VimMesh mesh) { - Meshes.Add(g); + Geometry.AddMesh(mesh); return this; } - public DocumentBuilder AddMeshes(IEnumerable gb) + public DocumentBuilder AddMeshes(IEnumerable meshes) { - Meshes.AddRange(gb); + foreach (var m in meshes) + { + AddMesh(m); + } return this; } - public DocumentBuilder AddInstances(IEnumerable ib) + public DocumentBuilder AddInstances(IEnumerable instances) { - Instances.AddRange(ib); + foreach (var m in instances) + { + Geometry.AddInstance(m); + } return this; } - public DocumentBuilder AddMaterials(IEnumerable mb) + + public DocumentBuilder AddInstance(Matrix4x4 transform, int meshIndex, int parentIndex = -1) { - Materials.AddRange(mb); + var instance = new Instance() + { + Transform = transform, + MeshIndex = meshIndex, + ParentIndex = parentIndex + }; + Geometry.AddInstance(instance); return this; } - public DocumentBuilder AddShapes(IEnumerable sb) + public DocumentBuilder AddMaterials(IEnumerable materials) { - Shapes.AddRange(sb); + foreach (var material in materials) + { + Geometry.AddMaterial(material); + } + return this; + } + + public DocumentBuilder AddShapes(IEnumerable shapes) + { + foreach (var shape in shapes) + { + Geometry.AddShape(shape); + } return this; } public DocumentBuilder AddAsset(INamedBuffer b) => AddAsset(b.Name, b.ToBytes()); - public DocumentBuilder AddInstance(Matrix4x4 transform, int meshIndex, int parentIndex = -1) - { - Instances.Add( - new Instance() - { - Transform = transform, - MeshIndex = meshIndex, - ParentIndex = parentIndex - } - ); - return this; - } public class StringLookupInfo { @@ -117,13 +127,13 @@ public StringLookupInfo(IEnumerable allStrings, int indexOffset = 0) } } - public static StringLookupInfo GetStringLookupInfo(IEnumerable tableBuilders, int indexOffset = 0) + private static StringLookupInfo GetStringLookupInfo(IEnumerable tableBuilders, int indexOffset = 0) => new StringLookupInfo(tableBuilders.SelectMany(tb => tb.GetAllStrings()), indexOffset); - public StringLookupInfo GetStringLookupInfo() + private StringLookupInfo GetStringLookupInfo() => GetStringLookupInfo(Tables.Values); - public List ComputeEntityTables(IReadOnlyDictionary stringLookup) + private List ComputeEntityTables(IReadOnlyDictionary stringLookup) { // Create the new Entity tables var tableList = new List(); @@ -134,17 +144,17 @@ public List ComputeEntityTables(IReadOnlyDictionary ComputeEntityTables(IReadOnlyDictionary g.Vertices.Count)); - tb.AddDataColumn("int:FaceCount", Meshes.Select(g => g.Indices.Count / 3)); + tb.AddDataColumn("int:VertexCount", Geometry.GetVertexCounts()); + tb.AddDataColumn("int:FaceCount", Geometry.GetFaceCounts()); } // TODO: add bounding box information to the nodes @@ -180,13 +190,15 @@ public List ComputeEntityTables(IReadOnlyDictionary kv.Value.ToNamedBuffer(kv.Key)) as IEnumerable; Debug.Assert(assets != null, "Asset conversion to IEnumerable failed."); @@ -195,7 +207,18 @@ public void Write(Stream stream) var entityTables = ComputeEntityTables(stringLookupInfo.StringLookup); var stringTable = stringLookupInfo.StringTable; - Serializer.Serialize(stream, Header, assets, stringTable, entityTables, new BigG3dWriter(Meshes, Instances, Shapes, Materials, null, UseColors)); + var doc = new SerializableDocument() + { + Header = Header, + Assets = assets.ToArray(), + StringTable = stringTable.ToArray(), + EntityTables = entityTables + }; + var bfast = doc.ToBFast(); + + bfast.SetBFast(BufferNames.Geometry, Geometry.ToBFast()); + + return bfast; } } } diff --git a/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs b/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs index 81e1c2d8..2ece68c5 100644 --- a/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs @@ -1,56 +1,28 @@ using System.Collections.Generic; using System.Linq; -using Vim.BFast; -using Vim.Format.Geometry; -using Vim.G3d; -using Vim.LinqArray; -using static Vim.Format.DocumentBuilder; namespace Vim.Format { public static class DocumentBuilderExtensions { - public static IMesh ToIMesh(this SubdividedMesh gb) - => gb.Vertices.ToIArray().TriMesh( - gb.Indices.ToIArray(), - submeshMaterials: gb.SubmeshMaterials.ToIArray()); - - public static Material ToDocumentBuilderMaterial(this G3dMaterial g3dMaterial) - => new Material - { - Color = g3dMaterial.Color, - Glossiness = g3dMaterial.Glossiness, - Smoothness = g3dMaterial.Smoothness, - }; - - public static SubdividedMesh ToDocumentBuilderSubdividedMesh(this IMesh m) - => new SubdividedMesh( - m.Indices.ToList(), - m.Vertices.ToList(), - m.SubmeshIndexOffsets.ToList(), - m.SubmeshMaterials.ToList()); - - public static void AddMesh(this DocumentBuilder db, IMesh m) - => db.AddMesh(m.ToDocumentBuilderSubdividedMesh()); - - public static EntityTableBuilder CreateTableCopy(this DocumentBuilder db, EntityTable table, List nodeIndexRemapping = null) + private static EntityTableBuilder CreateTableCopy(this DocumentBuilder db, EntityTable table, List nodeIndexRemapping = null) { var name = table.Name; var tb = db.CreateTableBuilder(name); - foreach (var col in table.IndexColumns.Values.ToEnumerable()) + foreach (var col in table.IndexColumns) { tb.AddIndexColumn(col.Name, col.GetTypedData().RemapData(nodeIndexRemapping)); } - foreach (var col in table.DataColumns.Values.ToEnumerable()) + foreach (var col in table.DataColumns) { tb.AddDataColumn(col.Name, col.CopyDataColumn(nodeIndexRemapping)); } - foreach (var col in table.StringColumns.Values.ToEnumerable()) + foreach (var col in table.StringColumns) { - var strings = col.GetTypedData().Select(i => table.Document.StringTable.ElementAtOrDefault(i, null)); + var strings = col.GetTypedData().Select(i => table.Document.GetString(i)); tb.AddStringColumn(col.Name, strings.ToArray().RemapData(nodeIndexRemapping)); } @@ -59,7 +31,7 @@ public static EntityTableBuilder CreateTableCopy(this DocumentBuilder db, Entity public static DocumentBuilder CopyTablesFrom(this DocumentBuilder db, Document doc, List nodeIndexRemapping = null) { - foreach (var table in doc.EntityTables.Values.ToEnumerable()) + foreach (var table in doc.Tables) { var name = table.Name; @@ -73,32 +45,6 @@ public static DocumentBuilder CopyTablesFrom(this DocumentBuilder db, Document d return db; } - public static SerializableEntityTable ToSerializableEntityTable(this EntityTableBuilder tb, - IReadOnlyDictionary stringLookup) - { - var table = new SerializableEntityTable - { - // Set the table name - Name = tb.Name, - - // Convert the columns to named buffers - IndexColumns = tb.IndexColumns - .Select(kv => kv.Value.ToNamedBuffer(kv.Key)) - .ToList(), - DataColumns = tb.DataColumns - .Select(kv => kv.Value.ToNamedBuffer(kv.Key) as INamedBuffer) - .ToList(), - StringColumns = tb.StringColumns - .Select(kv => kv.Value - .Select(s => stringLookup[s ?? string.Empty]) - .ToArray() - .ToNamedBuffer(kv.Key)) - .ToList(), - }; - - table.ValidateColumnRowsAreAligned(); - - return table; - } + } } diff --git a/src/cs/vim/Vim.Format.Core/DocumentBuilderTypes.cs b/src/cs/vim/Vim.Format.Core/DocumentBuilderTypes.cs index aa57fc46..06e49216 100644 --- a/src/cs/vim/Vim.Format.Core/DocumentBuilderTypes.cs +++ b/src/cs/vim/Vim.Format.Core/DocumentBuilderTypes.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Vim.Format.Geometry; using Vim.G3d; using Vim.Math3d; @@ -23,12 +24,12 @@ public class Shape public float Width; } - public class Material + public class Material : IMaterial { //RGBA - public Vector4 Color; - public float Glossiness; - public float Smoothness; + public Vector4 Color { get; set; } + public float Glossiness { get; set; } + public float Smoothness { get; set; } } /// @@ -36,19 +37,19 @@ public class Material /// public class Mesh { - protected List _vertices = new List(); + private readonly List _vertices; public IReadOnlyList Vertices => _vertices; - protected List _indices = new List(); + private readonly List _indices; public IReadOnlyList Indices => _indices; - protected List _faceMaterials = new List(); + private List _faceMaterials; public IReadOnlyList FaceMaterials => _faceMaterials; - protected List _colors = new List(); + private readonly List _colors; public IReadOnlyList Colors => _colors; - protected List _uvs = new List(); + private readonly List _uvs; public IReadOnlyList UVs => _uvs; public Mesh(List vertices = null, List indices = null, List faceMaterials = null, List colors = null, List uvs = null) @@ -71,15 +72,16 @@ public Mesh(List vertices = null, List indices = null, List f _uvs = uvs ?? new List(); } + public void SetMeshMaterial(int material) => _faceMaterials = Enumerable.Repeat(material, _indices.Count / 3).ToList(); - public void AppendFaces(IList indices, IList materials) + public void AppendFaces(int[] indices, int[] materials) { - if (indices.Count != materials.Count * 3) - throw new Exception("index.Count must be material.Count*3"); + if (indices.Length != materials.Length * 3) + throw new Exception("index.Length must be material.Length*3"); - for (var i = 0; i < materials.Count; i++) + for (var i = 0; i < materials.Length; i++) { var index = i * 3; AppendFace(indices[index], indices[index + 1], indices[index + 2], materials[i]); @@ -100,70 +102,39 @@ public void AppendVertices(IEnumerable vertices) public void AppendUVs(IEnumerable uvs) => _uvs.AddRange(uvs); - public SubdividedMesh Subdivide() - => new SubdividedMesh(this); - } - - /// - /// An immutable mesh were faces have been organized by submesh/material - /// - public class SubdividedMesh - { - public IReadOnlyList Indices { get; private set; } - public IReadOnlyList Vertices { get; private set; } - public IReadOnlyList SubmeshesIndexOffset { get; private set; } - public IReadOnlyList SubmeshMaterials { get; private set; } - - public SubdividedMesh(Mesh mesh) + public VimMesh Subdivide() { - if (mesh.Indices.Any(i => i < 0 && i >= mesh.Vertices.Count)) + if (Indices.Any(i => i < 0 && i >= Vertices.Count)) throw new Exception($"Invalid mesh. Indices out of vertex range."); - var facesByMats = mesh.FaceMaterials + var facesByMats = FaceMaterials .Select((face, index) => (face, index)) .GroupBy(pair => pair.face, pair => pair.index); - var submeshIndexOffset = new List(); + var submeshIndexOffsets = new List(); var submeshMaterials = new List(); var indicesRemap = new List(); foreach (var group in facesByMats) { - submeshIndexOffset.Add(indicesRemap.Count); + submeshIndexOffsets.Add(indicesRemap.Count); submeshMaterials.Add(group.Key); foreach (var face in group) { var f = face * 3; - indicesRemap.Add(mesh.Indices[f]); - indicesRemap.Add(mesh.Indices[f + 1]); - indicesRemap.Add(mesh.Indices[f + 2]); + indicesRemap.Add(Indices[f]); + indicesRemap.Add(Indices[f + 1]); + indicesRemap.Add(Indices[f + 2]); } } - Indices = indicesRemap; - SubmeshMaterials = submeshMaterials; - SubmeshesIndexOffset = submeshIndexOffset; - - Vertices = mesh.Vertices; - } + return new VimMesh( + indicesRemap.ToArray(), + Vertices.ToArray(), + submeshIndexOffsets.ToArray(), + submeshMaterials.ToArray() + ); - public SubdividedMesh( - IReadOnlyList indices, - IReadOnlyList vertices, - IReadOnlyList submeshesIndexOffset, - IReadOnlyList submeshMaterials - ) - { - Indices = indices; - Vertices = vertices; - SubmeshesIndexOffset = submeshesIndexOffset; - SubmeshMaterials = submeshMaterials; } - - public bool IsEquivalentTo(SubdividedMesh other) - => Vertices.SequenceEqual(other.Vertices) - && Indices.SequenceEqual(other.Indices) - && SubmeshesIndexOffset.SequenceEqual(other.SubmeshesIndexOffset) - && SubmeshMaterials.SequenceEqual(other.SubmeshMaterials); } } } diff --git a/src/cs/vim/Vim.Format.Core/DocumentExtensions.cs b/src/cs/vim/Vim.Format.Core/DocumentExtensions.cs deleted file mode 100644 index 53950875..00000000 --- a/src/cs/vim/Vim.Format.Core/DocumentExtensions.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Linq; -using System.Text.RegularExpressions; -using Vim.LinqArray; -using Vim.BFast; - -namespace Vim.Format -{ - public static class DocumentExtensions - { - public static Document ToDocument(this SerializableDocument document) - => new Document(document); - - public static EntityTable ToEntityTable(this SerializableEntityTable entityTable, Document document) - => new EntityTable(document, entityTable); - - public static string[] GetColumnNames(this EntityTable table) - => table.Columns.Select(b => b.Name).ToArray(); - - public static void ValidateRelations(this Document doc) - { - foreach (var et in doc.EntityTables.Values.ToEnumerable()) - { - foreach (var ic in et.IndexColumns.Values.ToEnumerable()) - { - var relatedTable = ic.GetRelatedTable(doc); - var maxValue = relatedTable.NumRows; - var data = ic.GetTypedData(); - for (var i = 0; i < data.Length; ++i) - { - var v = data[i]; - if (v < -1 || v > maxValue) - { - throw new Exception($"Invalid relation {v} out of range of -1 to {maxValue}"); - } - } - } - } - } - - public static readonly Regex IndexColumnNameComponentsRegex = new Regex(@"(\w+:)((?:\w|\.)+):(.+)"); - - public class IndexColumnNameComponents - { - public readonly string TypePrefix; - public readonly string TableName; - public readonly string FieldName; - - public IndexColumnNameComponents(string indexColumnName) - { - var match = IndexColumnNameComponentsRegex.Match(indexColumnName); - if (!match.Success) - throw new Exception($"Index column name {indexColumnName} could not be separated into its components."); - - TypePrefix = match.Groups[1].Value; - TableName = match.Groups[2].Value; - FieldName = match.Groups[3].Value; - } - } - - public static IndexColumnNameComponents SplitIndexColumnName(string name) - => new IndexColumnNameComponents(name); - - public static string GetRelatedTableNameFromColumnName(string name) - => SplitIndexColumnName(name).TableName; - - public static string GetFieldNameFromColumnName(string name) - => SplitIndexColumnName(name).FieldName; - - public static string GetFieldName(this INamedBuffer ic) - => GetFieldNameFromColumnName(ic.Name); - - public static string GetRelatedTableName(this INamedBuffer ic) - => GetRelatedTableNameFromColumnName(ic.Name); - - public static EntityTable GetRelatedTable(this INamedBuffer ic, Document doc) - => doc.GetTable(ic.GetRelatedTableName()); - - public static EntityTable GetTable(this Document doc, string name) - => doc.EntityTables.GetOrDefault(name); - - public static SerializableDocument SetFileName(this SerializableDocument doc, string fileName) - { - doc.FileName = fileName; - return doc; - } - } -} diff --git a/src/cs/vim/Vim.Format.Core/EntityColumnLoaderAttribute.cs b/src/cs/vim/Vim.Format.Core/EntityColumnLoaderAttribute.cs index 000c6d12..cb555817 100644 --- a/src/cs/vim/Vim.Format.Core/EntityColumnLoaderAttribute.cs +++ b/src/cs/vim/Vim.Format.Core/EntityColumnLoaderAttribute.cs @@ -5,7 +5,7 @@ namespace Vim.Format { [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] - public partial class EntityColumnLoaderAttribute : Attribute + public class EntityColumnLoaderAttribute : Attribute { /// /// The name of the serialized column. diff --git a/src/cs/vim/Vim.Format.Core/EntityTable.cs b/src/cs/vim/Vim.Format.Core/EntityTable.cs index 44360bf9..fe58b5b7 100644 --- a/src/cs/vim/Vim.Format.Core/EntityTable.cs +++ b/src/cs/vim/Vim.Format.Core/EntityTable.cs @@ -1,8 +1,8 @@ using System; -using System.Diagnostics; +using Vim.BFastLib; using System.Linq; -using Vim.BFast; -using Vim.LinqArray; +using System.Collections.Generic; +using Vim.Util; namespace Vim.Format { @@ -14,49 +14,72 @@ public EntityTable(Document document, SerializableEntityTable entityTable) _EntityTable = entityTable; Name = _EntityTable.Name; - DataColumns = LinqArray.LinqArray.ToLookup(_EntityTable.DataColumns, c => c.Name, c => c); - IndexColumns = LinqArray.LinqArray.ToLookup(_EntityTable.IndexColumns, c => c.Name, c => c); - StringColumns = LinqArray.LinqArray.ToLookup(_EntityTable.StringColumns, c => c.Name, c => c); - Columns = entityTable.ValidateColumnRowsAreAligned(); + _dataColumns = _EntityTable.DataColumns.ToDictionary( c => c.Name, c => c); + _indexColumns = _EntityTable.IndexColumns.ToDictionary(c => c.Name, c => c); + _stringColumns = _EntityTable.StringColumns.ToDictionary(c => c.Name, c => c); NumRows = Columns.FirstOrDefault()?.NumElements() ?? 0; + + Columns.ValidateColumnRowsAreAligned(); } private SerializableEntityTable _EntityTable { get; } public Document Document { get; } public string Name { get; } public int NumRows { get; } - public LinqArray.ILookup DataColumns { get; } - public LinqArray.ILookup> StringColumns { get; } - public LinqArray.ILookup> IndexColumns { get; } - public INamedBuffer[] Columns { get; } + private Dictionary _dataColumns { get; } + private Dictionary> _stringColumns { get; } + private Dictionary> _indexColumns { get; } + + + // Data + public bool HasDataColumns(string name) => _dataColumns.ContainsKey(name); + public IEnumerable DataColumns => _dataColumns.Values; + public IEnumerable DataColumnNames => _dataColumns.Keys; + + // Strings + public IEnumerable> StringColumns => _stringColumns.Values; + public bool HasStringColumn(string name) => _stringColumns.ContainsKey(name); + public NamedBuffer GetStringColumn(string name) => _stringColumns.GetOrDefault(name); + public IEnumerable StringColumnNames => _stringColumns.Keys; + + //Data + public IEnumerable> IndexColumns => _indexColumns.Values; + public IEnumerable IndexColumnNames => _indexColumns.Keys; + public bool HasIndexColumn(string name) => _indexColumns.ContainsKey(name); + public NamedBuffer GetIndexColumn(string name) => _indexColumns.GetOrDefault(name); + + + public IEnumerable Columns + => DataColumns + .Concat(IndexColumns.Select(x => (INamedBuffer)x)) + .Concat(StringColumns.Select(x => (INamedBuffer)x)); - public IArray GetIndexColumnValues(string columnName) - => IndexColumns.GetOrDefault(columnName)?.GetColumnValues().ToIArray(); + public int[] GetIndexColumnValues(string columnName) + => GetIndexColumn(columnName)?.AsArray(); - public IArray GetStringColumnValues(string columnName) - => StringColumns.GetOrDefault(columnName) - ?.GetColumnValues() - ?.Select(Document.GetString) - .ToIArray(); + public string[] GetStringColumnValues(string columnName) + => _stringColumns.GetOrDefault(columnName) + ?.AsArray() + ?.Select(Document.GetString).ToArray(); - public IArray GetDataColumnValues(string columnName) where T : unmanaged + public T[] GetDataColumnValues(string columnName) where T : unmanaged { var type = typeof(T); if (!ColumnExtensions.DataColumnTypes.Contains(type)) throw new Exception($"{nameof(GetDataColumnValues)} error - unsupported data column type {type}"); - var namedBuffer = DataColumns.GetOrDefault(columnName); + var namedBuffer = _dataColumns.GetOrDefault(columnName); if (namedBuffer == null) return null; if (type == typeof(short)) - return namedBuffer.GetColumnValues().Select(i => (short)i).ToIArray() as IArray; + return namedBuffer.AsArray().Select(i => (short)i) as T[]; if (type == typeof(bool)) - return namedBuffer.GetColumnValues().Select(b => b != 0).ToIArray() as IArray; + return namedBuffer.AsArray().Select(b => b != 0) as T[]; - return namedBuffer.GetColumnValues().ToIArray(); + return namedBuffer.AsArray(); } } } diff --git a/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs b/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs index bd1da0fd..4a30b671 100644 --- a/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs +++ b/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Vim.BFast; +using Vim.BFastLib; namespace Vim.Format { @@ -17,20 +17,20 @@ public class EntityTableBuilder public EntityTableBuilder(string name) => Name = name; - public EntityTableBuilder UpdateOrValidateRows(int n) + private EntityTableBuilder UpdateOrValidateRows(int n) { if (NumRows == 0) NumRows = n; else if (NumRows != n) throw new Exception($"Value count {n} does not match the expected number of rows {NumRows}"); return this; } - public void ValidateHasDataColumnPrefix(string columnName) + private static void ValidateHasDataColumnPrefix(string columnName) { if (!ColumnExtensions.IsDataColumnName(columnName)) throw new Exception($"{nameof(columnName)} {columnName} does not begin with a data column prefix"); } - public void ValidateHasPrefix(string columnName, string expectedPrefix) + private static void ValidateHasPrefix(string columnName, string expectedPrefix) { if (!columnName.StartsWith(expectedPrefix)) throw new Exception($"{nameof(columnName)} {columnName} must start with {expectedPrefix}"); @@ -97,6 +97,33 @@ public IEnumerable GetAllStrings() => StringColumns.Values.SelectMany(sc => sc) .Where(x => x != null); + public SerializableEntityTable ToSerializableEntityTable(IReadOnlyDictionary stringLookup) + { + var table = new SerializableEntityTable + { + // Set the table name + Name = Name, + + // Convert the columns to named buffers + IndexColumns = IndexColumns + .Select(kv => kv.Value.ToNamedBuffer(kv.Key)) + .ToList(), + DataColumns = DataColumns + .Select(kv => kv.Value.ToNamedBuffer(kv.Key) as INamedBuffer) + .ToList(), + StringColumns = StringColumns + .Select(kv => kv.Value + .Select(s => stringLookup[s ?? string.Empty]) + .ToArray() + .ToNamedBuffer(kv.Key)) + .ToList(), + }; + + table.ValidateColumnRowsAreAligned(); + + return table; + } + public void Clear() { NumRows = 0; diff --git a/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs b/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs index 775e2190..cb7b4a60 100644 --- a/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs +++ b/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Vim.BFast; +using Vim.BFastLib; using Vim.Util; namespace Vim.Format @@ -37,7 +37,7 @@ public EntityTable_v2( string[] stringBuffer) { Name = et.Name; - Columns = et.ValidateColumnRowsAreAligned(); + Columns = et.ValidateColumnRowsAreAligned().AllColumns.ToArray(); RowCount = Columns.FirstOrDefault()?.NumElements() ?? 0; foreach (var column in et.IndexColumns) diff --git a/src/cs/vim/Vim.Format.Core/G3dBuilder.cs b/src/cs/vim/Vim.Format.Core/G3dBuilder.cs new file mode 100644 index 00000000..9f75eaad --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/G3dBuilder.cs @@ -0,0 +1,134 @@ +using System.Collections.Generic; +using System.Linq; +using Vim.G3d; +using Vim.BFastLib; +using static Vim.Format.DocumentBuilder; +using Vim.Math3d; +using Vim.Format.Geometry; + +namespace Vim.Format +{ + public class G3dBuilder + { + private readonly List _meshes = new List(); + private readonly List _instances = new List(); + private readonly List _shapes = new List(); + private readonly List _materials = new List(); + + public void AddMesh(VimMesh mesh) + { + _meshes.Add(mesh); + } + + public void AddMeshes(IEnumerable meshes) + { + foreach (var mesh in meshes) + { + AddMesh(mesh); + } + } + + public void AddInstance(Instance instance) + { + _instances.Add(instance); + } + + public void AddInstances(IEnumerable instances) + { + foreach (var instance in instances) + { + AddInstance(instance); + } + } + + public void AddShape(Shape shape) + { + _shapes.Add(shape); + } + + public void AddMaterial(IMaterial material) + { + _materials.Add(material); + } + + public int InstanceCount => _instances.Count; + public int MeshCount => _meshes.Count; + public int MaterialCount => _materials.Count; + public int ShapeCount => _shapes.Count; + + public VimMesh GetMesh(int index) => _meshes[index]; + public AABox GetBox(int meshIndex) + { + return AABox.Create(_meshes[meshIndex].vertices); + } + + public int[] GetVertexCounts() + { + return _meshes.Select(m => m.vertices.Length).ToArray(); + } + + public int[] GetFaceCounts() + { + return _meshes.Select(m => m.NumFaces).ToArray(); + } + + + public BFast ToBFast() + { + var bfast = new BFast(); + var totalSubmeshCount = _meshes.Select(s => s.SubmeshCount).Sum(); + + // Compute the Vertex offsets and index offsets + var meshVertexOffsets = new int[_meshes.Count]; + var meshIndexOffsets = new int[_meshes.Count]; + var submeshIndexOffsets = new int[totalSubmeshCount]; + var meshSubmeshOffset = new int[_meshes.Count]; + + var n = _meshes.Count; + + for (var i = 1; i < n; ++i) + { + meshVertexOffsets[i] = meshVertexOffsets[i - 1] + _meshes[i - 1].vertices.Length; + meshIndexOffsets[i] = meshIndexOffsets[i - 1] + _meshes[i - 1].indices.Length; + meshSubmeshOffset[i] = meshSubmeshOffset[i - 1] + _meshes[i - 1].SubmeshCount; + } + + var subIndex = 0; + var previousIndexCount = 0; + foreach (var geo in _meshes) + { + foreach (var sub in geo.submeshIndexOffsets) + { + submeshIndexOffsets[subIndex++] = sub + previousIndexCount; + } + previousIndexCount += geo.indices.Length; + } + + // Compute the shape vertex offsets + var numShapes = _shapes.Count; + var shapeVertexOffsets = new int[numShapes]; + for (var i = 1; i < numShapes; ++i) + { + shapeVertexOffsets[i] = shapeVertexOffsets[i - 1] + _shapes[i - 1].Vertices.Count; + } + + bfast.SetEnumerable(CommonAttributes.Position, () => _meshes.SelectMany(m => m.vertices)); + bfast.SetEnumerable(CommonAttributes.Index, () => _meshes.SelectMany((m, ix) => m.indices.Select(i => i + meshVertexOffsets[ix]))); + bfast.SetEnumerable(CommonAttributes.MeshSubmeshOffset, () => meshSubmeshOffset); + bfast.SetEnumerable(CommonAttributes.SubmeshIndexOffset, () => submeshIndexOffsets); + bfast.SetEnumerable(CommonAttributes.SubmeshMaterial, () => _meshes.SelectMany(s => s.submeshMaterials)); + bfast.SetEnumerable(CommonAttributes.InstanceFlags, () => _instances.Select(i => (ushort)i.InstanceFlags)); + bfast.SetEnumerable(CommonAttributes.InstanceParent, () => _instances.Select(i => i.ParentIndex)); + bfast.SetEnumerable(CommonAttributes.InstanceMesh, () => _instances.Select(i => i.MeshIndex)); + bfast.SetEnumerable(CommonAttributes.InstanceTransform, () => _instances.Select(i => i.Transform)); + bfast.SetEnumerable(CommonAttributes.ShapeVertex, () => _shapes.SelectMany(s => s.Vertices)); + bfast.SetEnumerable(CommonAttributes.ShapeVertexOffset, () => shapeVertexOffsets); + bfast.SetEnumerable(CommonAttributes.ShapeColor, () => _shapes.Select(s => s.Color)); + bfast.SetEnumerable(CommonAttributes.ShapeWidth, () => _shapes.Select(s => s.Width)); + bfast.SetEnumerable(CommonAttributes.MaterialColor, () => _materials.Select(i => i.Color)); + bfast.SetEnumerable(CommonAttributes.MaterialGlossiness, () => _materials.Select(i => i.Glossiness)); + bfast.SetEnumerable(CommonAttributes.MaterialSmoothness, () => _materials.Select(i => i.Smoothness)); + return bfast; + } + } +} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/ArrayOps.cs b/src/cs/vim/Vim.Format.Core/Geometry/ArrayOps.cs deleted file mode 100644 index 55bbcedb..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/ArrayOps.cs +++ /dev/null @@ -1,260 +0,0 @@ - -// AUTOGENERATED FILE: DO NOT EDIT -// This file is generated from ArrayOps.tt - - -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - public static class ArrayOps - { - - public static IArray< int > Add(this IArray< int > self, IArray< int > other) => self.Zip(other, MathOps.Add); - public static IArray< int > Add(this IArray< int > self, int scalar) => self.Select(x => MathOps.Add(x, scalar)); - public static IArray< int > Add(this int self, IArray< int > vector) => vector.Select(x => MathOps.Add(self, x)); - public static IArray< int > Multiply(this IArray< int > self, IArray< int > other) => self.Zip(other, MathOps.Multiply); - public static IArray< int > Multiply(this IArray< int > self, int scalar) => self.Select(x => MathOps.Multiply(x, scalar)); - public static IArray< int > Multiply(this int self, IArray< int > vector) => vector.Select(x => MathOps.Multiply(self, x)); - public static IArray< int > Subtract(this IArray< int > self, IArray< int > other) => self.Zip(other, MathOps.Subtract); - public static IArray< int > Subtract(this IArray< int > self, int scalar) => self.Select(x => MathOps.Subtract(x, scalar)); - public static IArray< int > Subtract(this int self, IArray< int > vector) => vector.Select(x => MathOps.Subtract(self, x)); - public static IArray< int > Divide(this IArray< int > self, IArray< int > other) => self.Zip(other, MathOps.Divide); - public static IArray< int > Divide(this IArray< int > self, int scalar) => self.Select(x => MathOps.Divide(x, scalar)); - public static IArray< int > Divide(this int self, IArray< int > vector) => vector.Select(x => MathOps.Divide(self, x)); - public static IArray< long > Add(this IArray< long > self, IArray< long > other) => self.Zip(other, MathOps.Add); - public static IArray< long > Add(this IArray< long > self, long scalar) => self.Select(x => MathOps.Add(x, scalar)); - public static IArray< long > Add(this long self, IArray< long > vector) => vector.Select(x => MathOps.Add(self, x)); - public static IArray< long > Multiply(this IArray< long > self, IArray< long > other) => self.Zip(other, MathOps.Multiply); - public static IArray< long > Multiply(this IArray< long > self, long scalar) => self.Select(x => MathOps.Multiply(x, scalar)); - public static IArray< long > Multiply(this long self, IArray< long > vector) => vector.Select(x => MathOps.Multiply(self, x)); - public static IArray< long > Subtract(this IArray< long > self, IArray< long > other) => self.Zip(other, MathOps.Subtract); - public static IArray< long > Subtract(this IArray< long > self, long scalar) => self.Select(x => MathOps.Subtract(x, scalar)); - public static IArray< long > Subtract(this long self, IArray< long > vector) => vector.Select(x => MathOps.Subtract(self, x)); - public static IArray< long > Divide(this IArray< long > self, IArray< long > other) => self.Zip(other, MathOps.Divide); - public static IArray< long > Divide(this IArray< long > self, long scalar) => self.Select(x => MathOps.Divide(x, scalar)); - public static IArray< long > Divide(this long self, IArray< long > vector) => vector.Select(x => MathOps.Divide(self, x)); - public static IArray< float > Add(this IArray< float > self, IArray< float > other) => self.Zip(other, MathOps.Add); - public static IArray< float > Add(this IArray< float > self, float scalar) => self.Select(x => MathOps.Add(x, scalar)); - public static IArray< float > Add(this float self, IArray< float > vector) => vector.Select(x => MathOps.Add(self, x)); - public static IArray< float > Multiply(this IArray< float > self, IArray< float > other) => self.Zip(other, MathOps.Multiply); - public static IArray< float > Multiply(this IArray< float > self, float scalar) => self.Select(x => MathOps.Multiply(x, scalar)); - public static IArray< float > Multiply(this float self, IArray< float > vector) => vector.Select(x => MathOps.Multiply(self, x)); - public static IArray< float > Subtract(this IArray< float > self, IArray< float > other) => self.Zip(other, MathOps.Subtract); - public static IArray< float > Subtract(this IArray< float > self, float scalar) => self.Select(x => MathOps.Subtract(x, scalar)); - public static IArray< float > Subtract(this float self, IArray< float > vector) => vector.Select(x => MathOps.Subtract(self, x)); - public static IArray< float > Divide(this IArray< float > self, IArray< float > other) => self.Zip(other, MathOps.Divide); - public static IArray< float > Divide(this IArray< float > self, float scalar) => self.Select(x => MathOps.Divide(x, scalar)); - public static IArray< float > Divide(this float self, IArray< float > vector) => vector.Select(x => MathOps.Divide(self, x)); - public static IArray< double > Add(this IArray< double > self, IArray< double > other) => self.Zip(other, MathOps.Add); - public static IArray< double > Add(this IArray< double > self, double scalar) => self.Select(x => MathOps.Add(x, scalar)); - public static IArray< double > Add(this double self, IArray< double > vector) => vector.Select(x => MathOps.Add(self, x)); - public static IArray< double > Multiply(this IArray< double > self, IArray< double > other) => self.Zip(other, MathOps.Multiply); - public static IArray< double > Multiply(this IArray< double > self, double scalar) => self.Select(x => MathOps.Multiply(x, scalar)); - public static IArray< double > Multiply(this double self, IArray< double > vector) => vector.Select(x => MathOps.Multiply(self, x)); - public static IArray< double > Subtract(this IArray< double > self, IArray< double > other) => self.Zip(other, MathOps.Subtract); - public static IArray< double > Subtract(this IArray< double > self, double scalar) => self.Select(x => MathOps.Subtract(x, scalar)); - public static IArray< double > Subtract(this double self, IArray< double > vector) => vector.Select(x => MathOps.Subtract(self, x)); - public static IArray< double > Divide(this IArray< double > self, IArray< double > other) => self.Zip(other, MathOps.Divide); - public static IArray< double > Divide(this IArray< double > self, double scalar) => self.Select(x => MathOps.Divide(x, scalar)); - public static IArray< double > Divide(this double self, IArray< double > vector) => vector.Select(x => MathOps.Divide(self, x)); - public static IArray< Vector2 > Add(this IArray< Vector2 > self, IArray< Vector2 > other) => self.Zip(other, MathOps.Add); - public static IArray< Vector2 > Add(this IArray< Vector2 > self, Vector2 scalar) => self.Select(x => MathOps.Add(x, scalar)); - public static IArray< Vector2 > Add(this Vector2 self, IArray< Vector2 > vector) => vector.Select(x => MathOps.Add(self, x)); - public static IArray< Vector2 > Multiply(this IArray< Vector2 > self, IArray< Vector2 > other) => self.Zip(other, MathOps.Multiply); - public static IArray< Vector2 > Multiply(this IArray< Vector2 > self, Vector2 scalar) => self.Select(x => MathOps.Multiply(x, scalar)); - public static IArray< Vector2 > Multiply(this Vector2 self, IArray< Vector2 > vector) => vector.Select(x => MathOps.Multiply(self, x)); - public static IArray< Vector2 > Subtract(this IArray< Vector2 > self, IArray< Vector2 > other) => self.Zip(other, MathOps.Subtract); - public static IArray< Vector2 > Subtract(this IArray< Vector2 > self, Vector2 scalar) => self.Select(x => MathOps.Subtract(x, scalar)); - public static IArray< Vector2 > Subtract(this Vector2 self, IArray< Vector2 > vector) => vector.Select(x => MathOps.Subtract(self, x)); - public static IArray< Vector2 > Divide(this IArray< Vector2 > self, IArray< Vector2 > other) => self.Zip(other, MathOps.Divide); - public static IArray< Vector2 > Divide(this IArray< Vector2 > self, Vector2 scalar) => self.Select(x => MathOps.Divide(x, scalar)); - public static IArray< Vector2 > Divide(this Vector2 self, IArray< Vector2 > vector) => vector.Select(x => MathOps.Divide(self, x)); - public static IArray< Vector3 > Add(this IArray< Vector3 > self, IArray< Vector3 > other) => self.Zip(other, MathOps.Add); - public static IArray< Vector3 > Add(this IArray< Vector3 > self, Vector3 scalar) => self.Select(x => MathOps.Add(x, scalar)); - public static IArray< Vector3 > Add(this Vector3 self, IArray< Vector3 > vector) => vector.Select(x => MathOps.Add(self, x)); - public static IArray< Vector3 > Multiply(this IArray< Vector3 > self, IArray< Vector3 > other) => self.Zip(other, MathOps.Multiply); - public static IArray< Vector3 > Multiply(this IArray< Vector3 > self, Vector3 scalar) => self.Select(x => MathOps.Multiply(x, scalar)); - public static IArray< Vector3 > Multiply(this Vector3 self, IArray< Vector3 > vector) => vector.Select(x => MathOps.Multiply(self, x)); - public static IArray< Vector3 > Subtract(this IArray< Vector3 > self, IArray< Vector3 > other) => self.Zip(other, MathOps.Subtract); - public static IArray< Vector3 > Subtract(this IArray< Vector3 > self, Vector3 scalar) => self.Select(x => MathOps.Subtract(x, scalar)); - public static IArray< Vector3 > Subtract(this Vector3 self, IArray< Vector3 > vector) => vector.Select(x => MathOps.Subtract(self, x)); - public static IArray< Vector3 > Divide(this IArray< Vector3 > self, IArray< Vector3 > other) => self.Zip(other, MathOps.Divide); - public static IArray< Vector3 > Divide(this IArray< Vector3 > self, Vector3 scalar) => self.Select(x => MathOps.Divide(x, scalar)); - public static IArray< Vector3 > Divide(this Vector3 self, IArray< Vector3 > vector) => vector.Select(x => MathOps.Divide(self, x)); - public static IArray< Vector4 > Add(this IArray< Vector4 > self, IArray< Vector4 > other) => self.Zip(other, MathOps.Add); - public static IArray< Vector4 > Add(this IArray< Vector4 > self, Vector4 scalar) => self.Select(x => MathOps.Add(x, scalar)); - public static IArray< Vector4 > Add(this Vector4 self, IArray< Vector4 > vector) => vector.Select(x => MathOps.Add(self, x)); - public static IArray< Vector4 > Multiply(this IArray< Vector4 > self, IArray< Vector4 > other) => self.Zip(other, MathOps.Multiply); - public static IArray< Vector4 > Multiply(this IArray< Vector4 > self, Vector4 scalar) => self.Select(x => MathOps.Multiply(x, scalar)); - public static IArray< Vector4 > Multiply(this Vector4 self, IArray< Vector4 > vector) => vector.Select(x => MathOps.Multiply(self, x)); - public static IArray< Vector4 > Subtract(this IArray< Vector4 > self, IArray< Vector4 > other) => self.Zip(other, MathOps.Subtract); - public static IArray< Vector4 > Subtract(this IArray< Vector4 > self, Vector4 scalar) => self.Select(x => MathOps.Subtract(x, scalar)); - public static IArray< Vector4 > Subtract(this Vector4 self, IArray< Vector4 > vector) => vector.Select(x => MathOps.Subtract(self, x)); - public static IArray< Vector4 > Divide(this IArray< Vector4 > self, IArray< Vector4 > other) => self.Zip(other, MathOps.Divide); - public static IArray< Vector4 > Divide(this IArray< Vector4 > self, Vector4 scalar) => self.Select(x => MathOps.Divide(x, scalar)); - public static IArray< Vector4 > Divide(this Vector4 self, IArray< Vector4 > vector) => vector.Select(x => MathOps.Divide(self, x)); - - public static IArray Abs (this IArray< double > self) => self.Select(MathOps.Abs); - public static IArray Abs (this IArray< float > self) => self.Select(MathOps.Abs); - public static IArray Abs (this IArray< Vector2 > self) => self.Select(MathOps.Abs); - public static IArray Abs (this IArray< Vector3 > self) => self.Select(MathOps.Abs); - public static IArray Abs (this IArray< Vector4 > self) => self.Select(MathOps.Abs); - - public static IArray Acos (this IArray< double > self) => self.Select(MathOps.Acos); - public static IArray Acos (this IArray< float > self) => self.Select(MathOps.Acos); - public static IArray Acos (this IArray< Vector2 > self) => self.Select(MathOps.Acos); - public static IArray Acos (this IArray< Vector3 > self) => self.Select(MathOps.Acos); - public static IArray Acos (this IArray< Vector4 > self) => self.Select(MathOps.Acos); - - public static IArray Asin (this IArray< double > self) => self.Select(MathOps.Asin); - public static IArray Asin (this IArray< float > self) => self.Select(MathOps.Asin); - public static IArray Asin (this IArray< Vector2 > self) => self.Select(MathOps.Asin); - public static IArray Asin (this IArray< Vector3 > self) => self.Select(MathOps.Asin); - public static IArray Asin (this IArray< Vector4 > self) => self.Select(MathOps.Asin); - - public static IArray Atan (this IArray< double > self) => self.Select(MathOps.Atan); - public static IArray Atan (this IArray< float > self) => self.Select(MathOps.Atan); - public static IArray Atan (this IArray< Vector2 > self) => self.Select(MathOps.Atan); - public static IArray Atan (this IArray< Vector3 > self) => self.Select(MathOps.Atan); - public static IArray Atan (this IArray< Vector4 > self) => self.Select(MathOps.Atan); - - public static IArray Cos (this IArray< double > self) => self.Select(MathOps.Cos); - public static IArray Cos (this IArray< float > self) => self.Select(MathOps.Cos); - public static IArray Cos (this IArray< Vector2 > self) => self.Select(MathOps.Cos); - public static IArray Cos (this IArray< Vector3 > self) => self.Select(MathOps.Cos); - public static IArray Cos (this IArray< Vector4 > self) => self.Select(MathOps.Cos); - - public static IArray Cosh (this IArray< double > self) => self.Select(MathOps.Cosh); - public static IArray Cosh (this IArray< float > self) => self.Select(MathOps.Cosh); - public static IArray Cosh (this IArray< Vector2 > self) => self.Select(MathOps.Cosh); - public static IArray Cosh (this IArray< Vector3 > self) => self.Select(MathOps.Cosh); - public static IArray Cosh (this IArray< Vector4 > self) => self.Select(MathOps.Cosh); - - public static IArray Exp (this IArray< double > self) => self.Select(MathOps.Exp); - public static IArray Exp (this IArray< float > self) => self.Select(MathOps.Exp); - public static IArray Exp (this IArray< Vector2 > self) => self.Select(MathOps.Exp); - public static IArray Exp (this IArray< Vector3 > self) => self.Select(MathOps.Exp); - public static IArray Exp (this IArray< Vector4 > self) => self.Select(MathOps.Exp); - - public static IArray Log (this IArray< double > self) => self.Select(MathOps.Log); - public static IArray Log (this IArray< float > self) => self.Select(MathOps.Log); - public static IArray Log (this IArray< Vector2 > self) => self.Select(MathOps.Log); - public static IArray Log (this IArray< Vector3 > self) => self.Select(MathOps.Log); - public static IArray Log (this IArray< Vector4 > self) => self.Select(MathOps.Log); - - public static IArray Log10 (this IArray< double > self) => self.Select(MathOps.Log10); - public static IArray Log10 (this IArray< float > self) => self.Select(MathOps.Log10); - public static IArray Log10 (this IArray< Vector2 > self) => self.Select(MathOps.Log10); - public static IArray Log10 (this IArray< Vector3 > self) => self.Select(MathOps.Log10); - public static IArray Log10 (this IArray< Vector4 > self) => self.Select(MathOps.Log10); - - public static IArray Sin (this IArray< double > self) => self.Select(MathOps.Sin); - public static IArray Sin (this IArray< float > self) => self.Select(MathOps.Sin); - public static IArray Sin (this IArray< Vector2 > self) => self.Select(MathOps.Sin); - public static IArray Sin (this IArray< Vector3 > self) => self.Select(MathOps.Sin); - public static IArray Sin (this IArray< Vector4 > self) => self.Select(MathOps.Sin); - - public static IArray Sinh (this IArray< double > self) => self.Select(MathOps.Sinh); - public static IArray Sinh (this IArray< float > self) => self.Select(MathOps.Sinh); - public static IArray Sinh (this IArray< Vector2 > self) => self.Select(MathOps.Sinh); - public static IArray Sinh (this IArray< Vector3 > self) => self.Select(MathOps.Sinh); - public static IArray Sinh (this IArray< Vector4 > self) => self.Select(MathOps.Sinh); - - public static IArray Sqrt (this IArray< double > self) => self.Select(MathOps.Sqrt); - public static IArray Sqrt (this IArray< float > self) => self.Select(MathOps.Sqrt); - public static IArray Sqrt (this IArray< Vector2 > self) => self.Select(MathOps.Sqrt); - public static IArray Sqrt (this IArray< Vector3 > self) => self.Select(MathOps.Sqrt); - public static IArray Sqrt (this IArray< Vector4 > self) => self.Select(MathOps.Sqrt); - - public static IArray Tan (this IArray< double > self) => self.Select(MathOps.Tan); - public static IArray Tan (this IArray< float > self) => self.Select(MathOps.Tan); - public static IArray Tan (this IArray< Vector2 > self) => self.Select(MathOps.Tan); - public static IArray Tan (this IArray< Vector3 > self) => self.Select(MathOps.Tan); - public static IArray Tan (this IArray< Vector4 > self) => self.Select(MathOps.Tan); - - public static IArray Tanh (this IArray< double > self) => self.Select(MathOps.Tanh); - public static IArray Tanh (this IArray< float > self) => self.Select(MathOps.Tanh); - public static IArray Tanh (this IArray< Vector2 > self) => self.Select(MathOps.Tanh); - public static IArray Tanh (this IArray< Vector3 > self) => self.Select(MathOps.Tanh); - public static IArray Tanh (this IArray< Vector4 > self) => self.Select(MathOps.Tanh); - - public static IArray Sqr (this IArray< double > self) => self.Select(MathOps.Sqr); - public static IArray Sqr (this IArray< float > self) => self.Select(MathOps.Sqr); - public static IArray Sqr (this IArray< Vector2 > self) => self.Select(MathOps.Sqr); - public static IArray Sqr (this IArray< Vector3 > self) => self.Select(MathOps.Sqr); - public static IArray Sqr (this IArray< Vector4 > self) => self.Select(MathOps.Sqr); - - public static IArray Inverse (this IArray< double > self) => self.Select(MathOps.Inverse); - public static IArray Inverse (this IArray< float > self) => self.Select(MathOps.Inverse); - public static IArray Inverse (this IArray< Vector2 > self) => self.Select(MathOps.Inverse); - public static IArray Inverse (this IArray< Vector3 > self) => self.Select(MathOps.Inverse); - public static IArray Inverse (this IArray< Vector4 > self) => self.Select(MathOps.Inverse); - - public static IArray Ceiling (this IArray< double > self) => self.Select(MathOps.Ceiling); - public static IArray Ceiling (this IArray< float > self) => self.Select(MathOps.Ceiling); - public static IArray Ceiling (this IArray< Vector2 > self) => self.Select(MathOps.Ceiling); - public static IArray Ceiling (this IArray< Vector3 > self) => self.Select(MathOps.Ceiling); - public static IArray Ceiling (this IArray< Vector4 > self) => self.Select(MathOps.Ceiling); - - public static IArray Floor (this IArray< double > self) => self.Select(MathOps.Floor); - public static IArray Floor (this IArray< float > self) => self.Select(MathOps.Floor); - public static IArray Floor (this IArray< Vector2 > self) => self.Select(MathOps.Floor); - public static IArray Floor (this IArray< Vector3 > self) => self.Select(MathOps.Floor); - public static IArray Floor (this IArray< Vector4 > self) => self.Select(MathOps.Floor); - - public static IArray Round (this IArray< double > self) => self.Select(MathOps.Round); - public static IArray Round (this IArray< float > self) => self.Select(MathOps.Round); - public static IArray Round (this IArray< Vector2 > self) => self.Select(MathOps.Round); - public static IArray Round (this IArray< Vector3 > self) => self.Select(MathOps.Round); - public static IArray Round (this IArray< Vector4 > self) => self.Select(MathOps.Round); - - public static IArray Truncate (this IArray< double > self) => self.Select(MathOps.Truncate); - public static IArray Truncate (this IArray< float > self) => self.Select(MathOps.Truncate); - public static IArray Truncate (this IArray< Vector2 > self) => self.Select(MathOps.Truncate); - public static IArray Truncate (this IArray< Vector3 > self) => self.Select(MathOps.Truncate); - public static IArray Truncate (this IArray< Vector4 > self) => self.Select(MathOps.Truncate); - // TODO: do this over all over the numerical types - public static long Sum(this IArray self) => self.Aggregate(0L, (x, y) => x + y); - public static long Sum(this IArray self) => self.Aggregate(0L, (x, y) => x + y); - public static double Sum(this IArray self) => self.Aggregate(0.0, (x, y) => x + y); - public static double Sum(this IArray self) => self.Aggregate(0.0, (x, y) => x + y); - public static Vector2 Sum(this IArray self) => self.Aggregate(Vector2.Zero, (x, y) => x + y); - public static Vector3 Sum(this IArray self) => self.Aggregate(Vector3.Zero, (x, y) => x + y); - public static Vector4 Sum(this IArray self) => self.Aggregate(Vector4.Zero, (x, y) => x + y); - - public static double Average(this IArray self) => self.Sum() / self.Count; - public static double Average(this IArray self) => self.Sum() / self.Count; - public static double Average(this IArray self) => self.Sum() / self.Count; - public static double Average(this IArray self) => self.Sum() / self.Count; - public static Vector2 Average(this IArray self) => self.Sum() / self.Count; - public static Vector3 Average(this IArray self) => self.Sum() / self.Count; - public static Vector4 Average(this IArray self) => self.Sum() / self.Count; - - public static double Variance(this IArray self) { var mean = self.Average(); return self.Select(x => MathOps.Sqr(x - mean)).Average(); } - public static double Variance(this IArray self) { var mean = self.Average(); return self.Select(x => MathOps.Sqr(x - mean)).Average(); } - public static double Variance(this IArray self) { var mean = self.Average(); return self.Select(x => MathOps.Sqr(x - mean)).Average(); } - public static double Variance(this IArray self) { var mean = self.Average(); return self.Select(x => MathOps.Sqr(x - mean)).Average(); } - public static Vector2 Variance(this IArray self) { var mean = self.Average(); return self.Select(x => MathOps.Sqr(x - mean)).Average(); } - public static Vector3 Variance(this IArray self) { var mean = self.Average(); return self.Select(x => MathOps.Sqr(x - mean)).Average(); } - public static Vector4 Variance(this IArray self) { var mean = self.Average(); return self.Select(x => MathOps.Sqr(x - mean)).Average(); } - - public static double StdDev(this IArray self) => self.Variance().Sqrt(); - public static double StdDev(this IArray self) => self.Variance().Sqrt(); - public static double StdDev(this IArray self) => self.Variance().Sqrt(); - public static double StdDev(this IArray self) => self.Variance().Sqrt(); - public static Vector2 StdDev(this IArray self) => self.Variance().Sqrt(); - public static Vector3 StdDev(this IArray self) => self.Variance().Sqrt(); - public static Vector4 StdDev(this IArray self) => self.Variance().Sqrt(); - - public static IArray PartialSums(this IArray self) => self.PostAccumulate((x, y) => x + y); - public static IArray PartialSums(this IArray self) => self.PostAccumulate((x, y) => x + y); - public static IArray PartialSums(this IArray self) => self.PostAccumulate((x, y) => x + y); - public static IArray PartialSums(this IArray self) => self.PostAccumulate((x, y) => x + y); - public static IArray PartialSums(this IArray self) => self.PostAccumulate((x, y) => x + y); - public static IArray PartialSums(this IArray self) => self.PostAccumulate((x, y) => x + y); - public static IArray PartialSums(this IArray self) => self.PostAccumulate((x, y) => x + y); - - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/ArrayOps.tt b/src/cs/vim/Vim.Format.Core/Geometry/ArrayOps.tt deleted file mode 100644 index 2858a417..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/ArrayOps.tt +++ /dev/null @@ -1,96 +0,0 @@ -<#@ template language="C#" #> -<#@ output extension=".cs" #> -<#@ assembly name="System.Core" #> -<#@ import namespace="System" #> -<#@ import namespace="System.IO" #> -<#@ import namespace="System.Diagnostics" #> -<#@ import namespace="System.Linq" #> - -// AUTOGENERATED FILE: DO NOT EDIT -// This file is generated from ArrayOps.tt - -<# -var nonVectorTypes = new[] { "int", "long", "float", "double" }; -var vectorTypes = new[] { "Vector2", "Vector3", "Vector4" }; -var types = nonVectorTypes.Concat(vectorTypes); -var intTypes = new[] { "int", "long" }; -var araBinaryOps = new[] { "Add", "Sub", "Mul", "Div" }; -var araCompOps = new[] { "Gt", "Lt", "GtEq", "LtEq", "Eq", "NEq" }; -var sysUnaryOps = new[] { "Abs", "Acos", "Asin", "Atan", "Cos", "Cosh", "Exp", "Log", "Log10", "Sin", "Sinh", "Sqrt", "Tan", "Tanh" }; -var araUnaryOps = new[] { "Sqr", "Inverse", "Ceiling", "Floor", "Round", "Truncate" }; -var allUnaryOps = sysUnaryOps.Concat(araUnaryOps); -#> - -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - public static class ArrayOps - { - -<# -foreach (var t in types) { -foreach (var op in new[] { "Add", "Multiply", "Subtract", "Divide" }) -{ -#> - public static IArray< <#= t #> > <#= op #>(this IArray< <#= t #> > self, IArray< <#= t #> > other) => self.Zip(other, MathOps.<#= op #>); - public static IArray< <#= t #> > <#= op #>(this IArray< <#= t #> > self, <#= t #> scalar) => self.Select(x => MathOps.<#= op #>(x, scalar)); - public static IArray< <#= t #> > <#= op #>(this <#= t #> self, IArray< <#= t #> > vector) => vector.Select(x => MathOps.<#= op #>(self, x)); -<# -} -} -foreach (var op in allUnaryOps) { -#> - - public static IArray <#= op #> (this IArray< double > self) => self.Select(MathOps.<#= op #>); - public static IArray <#= op #> (this IArray< float > self) => self.Select(MathOps.<#= op #>); - public static IArray <#= op #> (this IArray< Vector2 > self) => self.Select(MathOps.<#= op #>); - public static IArray <#= op #> (this IArray< Vector3 > self) => self.Select(MathOps.<#= op #>); - public static IArray <#= op #> (this IArray< Vector4 > self) => self.Select(MathOps.<#= op #>); -<# -} -#> - // TODO: do this over all over the numerical types - public static long Sum(this IArray self) => self.Aggregate(0L, (x, y) => x + y); - public static long Sum(this IArray self) => self.Aggregate(0L, (x, y) => x + y); - public static double Sum(this IArray self) => self.Aggregate(0.0, (x, y) => x + y); - public static double Sum(this IArray self) => self.Aggregate(0.0, (x, y) => x + y); - public static Vector2 Sum(this IArray self) => self.Aggregate(Vector2.Zero, (x, y) => x + y); - public static Vector3 Sum(this IArray self) => self.Aggregate(Vector3.Zero, (x, y) => x + y); - public static Vector4 Sum(this IArray self) => self.Aggregate(Vector4.Zero, (x, y) => x + y); - - public static double Average(this IArray self) => self.Sum() / self.Count; - public static double Average(this IArray self) => self.Sum() / self.Count; - public static double Average(this IArray self) => self.Sum() / self.Count; - public static double Average(this IArray self) => self.Sum() / self.Count; - public static Vector2 Average(this IArray self) => self.Sum() / self.Count; - public static Vector3 Average(this IArray self) => self.Sum() / self.Count; - public static Vector4 Average(this IArray self) => self.Sum() / self.Count; - - public static double Variance(this IArray self) { var mean = self.Average(); return self.Select(x => MathOps.Sqr(x - mean)).Average(); } - public static double Variance(this IArray self) { var mean = self.Average(); return self.Select(x => MathOps.Sqr(x - mean)).Average(); } - public static double Variance(this IArray self) { var mean = self.Average(); return self.Select(x => MathOps.Sqr(x - mean)).Average(); } - public static double Variance(this IArray self) { var mean = self.Average(); return self.Select(x => MathOps.Sqr(x - mean)).Average(); } - public static Vector2 Variance(this IArray self) { var mean = self.Average(); return self.Select(x => MathOps.Sqr(x - mean)).Average(); } - public static Vector3 Variance(this IArray self) { var mean = self.Average(); return self.Select(x => MathOps.Sqr(x - mean)).Average(); } - public static Vector4 Variance(this IArray self) { var mean = self.Average(); return self.Select(x => MathOps.Sqr(x - mean)).Average(); } - - public static double StdDev(this IArray self) => self.Variance().Sqrt(); - public static double StdDev(this IArray self) => self.Variance().Sqrt(); - public static double StdDev(this IArray self) => self.Variance().Sqrt(); - public static double StdDev(this IArray self) => self.Variance().Sqrt(); - public static Vector2 StdDev(this IArray self) => self.Variance().Sqrt(); - public static Vector3 StdDev(this IArray self) => self.Variance().Sqrt(); - public static Vector4 StdDev(this IArray self) => self.Variance().Sqrt(); - - public static IArray PartialSums(this IArray self) => self.PostAccumulate((x, y) => x + y); - public static IArray PartialSums(this IArray self) => self.PostAccumulate((x, y) => x + y); - public static IArray PartialSums(this IArray self) => self.PostAccumulate((x, y) => x + y); - public static IArray PartialSums(this IArray self) => self.PostAccumulate((x, y) => x + y); - public static IArray PartialSums(this IArray self) => self.PostAccumulate((x, y) => x + y); - public static IArray PartialSums(this IArray self) => self.PostAccumulate((x, y) => x + y); - public static IArray PartialSums(this IArray self) => self.PostAccumulate((x, y) => x + y); - - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/CatmullClark.cs b/src/cs/vim/Vim.Format.Core/Geometry/CatmullClark.cs deleted file mode 100644 index 4ffbd54a..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/CatmullClark.cs +++ /dev/null @@ -1,230 +0,0 @@ -using System; -using System.Collections.Generic; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - // This is used to index an edge from any two vertices. - // Vertex order doesnt matter. - public class GeometryEdgeKey : IEquatable - { - public int Vertex0 { get; } - public int Vertex1 { get; } - - public GeometryEdgeKey(int v0, int v1) - { - Vertex0 = Math.Min(v0, v1); - Vertex1 = Math.Max(v0, v1); - } - - public override int GetHashCode() - => Hash.Combine(Vertex0.GetHashCode(), Vertex1.GetHashCode()); - - public override bool Equals(object obj) - => obj is GeometryEdgeKey && Equals((GeometryEdgeKey)obj); - - public bool Equals(GeometryEdgeKey p) - => Vertex0 == p.Vertex0 && Vertex1 == p.Vertex1; - } - - // Stores information about an edge in a mesh. - public class GeometryEdge - { - public Vector3 EdgePoint { get; set; } - public int EdgePointIndex { get; set; } = -1; - public int Face0 { get; set; } = -1; - public int Face1 { get; set; } = -1; - } - - public static class CatmullClarkSmoothing - { - public static Dictionary CreateEdgeMap(this IMesh geometry) - { - var globalEdgeIndex = 0; - var edgeMap = new Dictionary(); - var currentIndex = 0; - for (var faceIndex = 0; faceIndex < geometry.NumFaces; faceIndex++) - { - var faceSize = 3; - for (var edgeIndex = 0; edgeIndex < faceSize; edgeIndex++) - { - var geometryEdgeKey = new GeometryEdgeKey(geometry.Indices[currentIndex + edgeIndex], geometry.Indices[currentIndex + (edgeIndex + 1) % faceSize]); - if (edgeMap.ContainsKey(geometryEdgeKey)) - { - edgeMap[geometryEdgeKey].Face1 = faceIndex; - } - else - { - var geometryEdge = new GeometryEdge(); - geometryEdge.EdgePointIndex = globalEdgeIndex++; - geometryEdge.Face0 = faceIndex; - edgeMap[geometryEdgeKey] = geometryEdge; - } - } - currentIndex += faceSize; - } - - return edgeMap; - } - - public static IMesh CatmullClark(this IMesh geometry, float smoothing = 0.0f) - { - var edgeMap = geometry.CreateEdgeMap(); - - var numQuads = geometry.NumFaces * 3; - var numVertices = geometry.Vertices.Count + edgeMap.Count + geometry.NumFaces; - - var facePoints = new Vector3[geometry.NumFaces]; - var vertexFPoints = new Vector3[geometry.Vertices.Count]; - var vertexRPoints = new Vector3[geometry.Vertices.Count]; - var vertexNumFaces = new float[geometry.Vertices.Count]; - var vertexNumEdges = new float[geometry.Vertices.Count]; - var newVertices = new Vector3[numVertices]; - var newIndices = new int[numQuads * 4]; - var edgeVertices = new bool[geometry.Vertices.Count]; - - foreach (var edge in edgeMap) - { - if (edge.Value.Face0 == -1 || edge.Value.Face1 == -1) - { - edgeVertices[edge.Key.Vertex0] = true; - edgeVertices[edge.Key.Vertex1] = true; - } - } - - // For each face, add a face point - // Set each face point to be the average of all original points for the respective face. - // For each edge, add an edge point. - // Set each edge point to be the average of the two neighbouring face points and its two original endpoints. - // For each face point, add an edge for every edge of the face, connecting the face point to each edge point for the face. - // For each original point P, take the average F of all n (recently created) face points for faces touching P, and take - // the average R of all n edge midpoints for (original) edges touching P, where each edge midpoint is the average of its - // two endpoint vertices (not to be confused with new "edge points" above). Move each original point to the point - // - // (F + 2 R + ( n − 3 ) P) / n - // - // This is the barycenter of P, R and F with respective weights (n − 3), 2 and 1. - // - // Connect each new vertex point to the new edge points of all original edges incident on the original vertex. - // Define new faces as enclosed by edges. - - CalculateFaceCentroidPoints(geometry, facePoints, vertexNumFaces, vertexFPoints); - CalculateEdgePoints(geometry, edgeMap, facePoints, vertexNumEdges, vertexRPoints, smoothing); - CalculateVertexPoints(geometry, vertexNumFaces, vertexFPoints, vertexNumEdges, vertexRPoints, newVertices, edgeVertices, smoothing); - - var facePointStartIndex = geometry.Vertices.Count; - var edgePointStartIndex = facePointStartIndex + facePoints.Length; - - // Copy all points into a single buffer - for (var i = 0; i < facePoints.Length; i++) - { - newVertices[facePointStartIndex + i] = facePoints[i]; - } - foreach (var edge in edgeMap) - { - newVertices[edgePointStartIndex + edge.Value.EdgePointIndex] = edge.Value.EdgePoint; - } - - return GenerateMesh(geometry, newVertices, newIndices, edgeMap, facePointStartIndex, edgePointStartIndex, numQuads); - } - - public static void CalculateFaceCentroidPoints(IMesh geometry, Vector3[] outFacePoints, float[] outVertexNumFaces, Vector3[] outVertexFPoints) - { - var currentVertexIndex = 0; - for (var faceIndex = 0; faceIndex < geometry.NumFaces; faceIndex++) - { - var faceSize = geometry.NumCornersPerFace; - - var facePoint = new Vector3(); - - for (var edgeIndex = 0; edgeIndex < faceSize; edgeIndex++) - { - var vertexIndex = geometry.Indices[currentVertexIndex + edgeIndex]; - facePoint += geometry.Vertices[vertexIndex]; - } - - facePoint /= faceSize; - - outFacePoints[faceIndex] = facePoint; - - for (var edgeIndex = 0; edgeIndex < faceSize; edgeIndex++) - { - var vertexIndex = geometry.Indices[currentVertexIndex + edgeIndex]; - outVertexNumFaces[vertexIndex]++; - outVertexFPoints[vertexIndex] += facePoint; - } - - currentVertexIndex += faceSize; - } - } - - public static void CalculateEdgePoints(IMesh geometry, Dictionary edgeMap, Vector3[] facePoints, float[] outVertexNumEdges, Vector3[] outVertexRPoints, float smoothing) - { - foreach (var edge in edgeMap) - { - var n = 2.0f; - var middlePoint = geometry.Vertices[edge.Key.Vertex0] + geometry.Vertices[edge.Key.Vertex1]; - var edgePoint = middlePoint; - - if (edge.Value.Face0 >= 0 && edge.Value.Face1 >= 0) - { - edgePoint += facePoints[edge.Value.Face0]; - edgePoint += facePoints[edge.Value.Face1]; - n += 2.0f; - } - - middlePoint /= 2.0f; - edgePoint /= n; - - edge.Value.EdgePoint = edgePoint * smoothing + middlePoint * (1.0f - smoothing); - - outVertexNumEdges[edge.Key.Vertex0]++; - outVertexNumEdges[edge.Key.Vertex1]++; - outVertexRPoints[edge.Key.Vertex0] += edge.Value.EdgePoint; - outVertexRPoints[edge.Key.Vertex1] += edge.Value.EdgePoint; - } - } - - public static void CalculateVertexPoints(IMesh geometry, float[] vertexNumFaces, Vector3[] vertexFPoints, float[] vertexNumEdges, Vector3[] vertexRPoints, Vector3[] outNewVertices, bool[] edgeVertices, float smoothing) - { - for (var index = 0; index < geometry.Vertices.Count; index++) - { - var numFaces = vertexNumFaces[index]; - var numEdges = vertexNumEdges[index]; - var newVertex = (vertexFPoints[index] / numFaces + 2.0f * vertexRPoints[index] / numEdges + (numFaces - 3.0f) * geometry.Vertices[index]) / numFaces; - - outNewVertices[index] = edgeVertices[index] ? geometry.Vertices[index] : smoothing * newVertex + (1.0f - smoothing) * geometry.Vertices[index]; - } - } - - public static IMesh GenerateMesh(IMesh geometry, Vector3[] newVertices, int[] newIndices, Dictionary edgeMap, int facePointStartIndex, int edgePointStartIndex, int numQuads) - { - var currentNewVertexIndex = 0; - var currentOldVertexIndex = 0; - for (var faceIndex = 0; faceIndex < geometry.NumFaces; faceIndex++) - { - var faceSize = geometry.NumCornersPerFace; - - for (var i = 0; i < faceSize; i++) - { - var prevFaceVertexIndex = geometry.Indices[currentOldVertexIndex + i]; - var faceVertexIndex = geometry.Indices[currentOldVertexIndex + (i + 1) % faceSize]; - var nextFaceVertexIndex = geometry.Indices[currentOldVertexIndex + (i + 2) % faceSize]; - - var edgeKey0 = new GeometryEdgeKey(prevFaceVertexIndex, faceVertexIndex); - var edgeKey1 = new GeometryEdgeKey(faceVertexIndex, nextFaceVertexIndex); - - newIndices[currentNewVertexIndex++] = faceIndex + facePointStartIndex; - newIndices[currentNewVertexIndex++] = edgeMap[edgeKey0].EdgePointIndex + edgePointStartIndex; - newIndices[currentNewVertexIndex++] = faceVertexIndex; - newIndices[currentNewVertexIndex++] = edgeMap[edgeKey1].EdgePointIndex + edgePointStartIndex; - } - - currentOldVertexIndex += faceSize; - } - - return Primitives.QuadMesh(newVertices.ToIArray(), newIndices.ToIArray()); - } - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/GeometryCuttingUtils.cs b/src/cs/vim/Vim.Format.Core/Geometry/GeometryCuttingUtils.cs deleted file mode 100644 index 338f7776..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/GeometryCuttingUtils.cs +++ /dev/null @@ -1,173 +0,0 @@ -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - public static class GeometryCuttingUtils - { - // Fins the intersection between two lines. - // Returns true if they intersect - // t and u are the distances of the intersection point along the two line - public static bool LineLineIntersection(Vector2 line1Origin, Vector2 line1Direction, Vector2 line2Origin, Vector2 line2Direction, out float t, out float u, float epsilon = 0.01f) - { - var line1P2 = line1Origin + line1Direction; - var line2P2 = line2Origin + line2Direction; - - var x1 = line1Origin.X; - var y1 = line1Origin.Y; - var x2 = line1P2.X; - var y2 = line1P2.Y; - var x3 = line2Origin.X; - var y3 = line2Origin.Y; - var x4 = line2P2.X; - var y4 = line2P2.Y; - - var denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); - - if (denominator.Abs() < epsilon) - { - t = 0.0f; - u = 0.0f; - return false; - } - - var tNeumerator = (x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4); - var uNeumerator = (x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3); - - t = tNeumerator / denominator; - u = -uNeumerator / denominator; - - return true; - } - - // Returns the distance between two lines - // t and u are the distances if the intersection points along the two lines - public static float LineLineDistance(Vector2 line1A, Vector2 line1B, Vector2 line2A, Vector2 line2B, out float t, out float u, float epsilon = 0.0000001f) - { - var x1 = line1A.X; - var y1 = line1A.Y; - var x2 = line1B.X; - var y2 = line1B.Y; - var x3 = line2A.X; - var y3 = line2A.Y; - var x4 = line2B.X; - var y4 = line2B.Y; - - var denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); - - if (denominator.Abs() >= epsilon) - { - // Lines are not parallel, they should intersect nicely - var tNeumerator = (x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4); - var uNeumerator = (x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3); - - t = tNeumerator / denominator; - u = -uNeumerator / denominator; - - var e = 0.0; - if (t >= -e && t <= 1.0 + e && u >= -e && u <= 1.0 + e) - { - t = t.Clamp(0.0f, 1.0f); - u = u.Clamp(0.0f, 1.0f); - return 0; - } - } - - // Parallel or non intersecting lines - default to point to line checks - - u = 0.0f; - var minDistance = LinePointDistance(line1A, line1B, line2A, out t); - var distance = LinePointDistance(line1A, line1B, line2B, out var closestPoint); - if (distance < minDistance) - { - minDistance = distance; - t = closestPoint; - u = 1.0f; - } - - distance = LinePointDistance(line2A, line2B, line1A, out closestPoint); - if (distance < minDistance) - { - minDistance = distance; - u = closestPoint; - t = 0.0f; - } - - distance = LinePointDistance(line2A, line2B, line1B, out closestPoint); - if (distance < minDistance) - { - minDistance = distance; - u = closestPoint; - t = 1.0f; - } - - return minDistance; - } - - // Returns the distance between a line and a point. - // t is the distance along the line of the closest point - public static float LinePointDistance(Vector2 v, Vector2 w, Vector2 p, out float t) - { - // Return minimum distance between line segment vw and point p - var l2 = (v - w).LengthSquared(); // i.e. |w-v|^2 - avoid a sqrt - if (l2 == 0.0f) // v == w case - { - t = 0.5f; - return (p - v).Length(); - } - - // Consider the line extending the segment, parameterized as v + t (w - v). - // We find projection of point p onto the line. - // It falls where t = [(p-v) . (w-v)] / |w-v|^2 - // We clamp t from [0,1] to handle points outside the segment vw. - t = ((p - v).Dot(w - v) / l2).Clamp(0.0f, 1.0f); - var closestPoint = v + t * (w - v); // Projection falls on the segment - return (p - closestPoint).Length(); - } - - - - // Apply a surface function that projects a 2d triangulation in UV space into a 3d triangulation - /* public static IGeometry ApplySurfaceFunction(this List triangulations, Func surfaceFunction) - { - var allIndices = new List(); - var allVertices = new List(); - - foreach (var triangulation in triangulations) - { - var vertices = triangulation.Points.ToList(); - var indices = triangulation.TriangleIndices; - - while (true) - { - List outIndices; - List outVertices; - bool done = SubdivideLargeTriangles(vertices, indices, out outVertices, out outIndices, 0.3f); - - vertices = outVertices; - indices = outIndices; - - if (done) - { - break; - } - } - - // Apply surface function - var vertices3d = vertices.Select(x => surfaceFunction(x.ToVector())); - - // Merge all triangulations - int startIndex = allVertices.Count; - allVertices.AddRange(vertices3d); - allIndices.AddRange(indices.Select(x => x + startIndex)); - } - - var cutGeometry = new G3D( - allVertices.ToIArray().ToVertexAttribute(), - allIndices.ToIArray().ToIndexAttribute(), - new FunctionalArray(allIndices.Count / 3, x => 3).ToFaceSizeAttribute() - ).ToIMesh(); - - return cutGeometry; - }*/ - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/GeometryUtil.cs b/src/cs/vim/Vim.Format.Core/Geometry/GeometryUtil.cs deleted file mode 100644 index db8d33e7..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/GeometryUtil.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - // TODO: many of these functions should live in other places - public static class GeometryUtil - { - public static IArray Normalize(this IArray vectors) - => vectors.Select(v => v.Normalize()); - - public static bool SequenceAlmostEquals(this IArray vs1, IArray vs2, float tolerance = Math3d.Constants.Tolerance) - => vs1.Count == vs2.Count && vs1.Indices().All(i => vs1[i].AlmostEquals(vs2[i], tolerance)); - - public static bool AreColinear(this IEnumerable vectors, Vector3 reference, float tolerance = (float)Math3d.Constants.OneTenthOfADegree) - => !reference.IsNaN() && vectors.All(v => v.Colinear(reference, tolerance)); - - public static bool AreColinear(this IEnumerable vectors, float tolerance = (float)Math3d.Constants.OneTenthOfADegree) - => vectors.ToList().AreColinear(tolerance); - - public static bool AreColinear(this IList vectors, float tolerance = (float)Math3d.Constants.OneTenthOfADegree) - => vectors.Count <= 1 || vectors.Skip(1).AreColinear(vectors[0], tolerance); - - public static AABox BoundingBox(this IArray vertices) - => AABox.Create(vertices.ToEnumerable()); - - public static IArray SampleZeroToOneInclusive(this int count) - => (count + 1).Select(i => i / (float)count); - - public static IArray SampleZeroToOneExclusive(this int count) - => count.Select(i => i / (float)count); - - public static IArray InterpolateInclusive(this int count, Func function) - => count.SampleZeroToOneInclusive().Select(function); - - public static IArray Interpolate(this Line self, int count) - => count.InterpolateInclusive(self.Lerp); - - public static IArray Rotate(this IArray self, Vector3 axis, float angle) - => self.Transform(Matrix4x4.CreateFromAxisAngle(axis, angle)); - - public static IArray Transform(this IArray self, Matrix4x4 matrix) - => self.Select(x => x.Transform(matrix)); - - public static Int3 Sort(this Int3 v) - { - if (v.X < v.Y) - { - return (v.Y < v.Z) - ? new Int3(v.X, v.Y, v.Z) - : (v.X < v.Z) - ? new Int3(v.X, v.Z, v.Y) - : new Int3(v.Z, v.X, v.Y); - } - else - { - return (v.X < v.Z) - ? new Int3(v.Y, v.X, v.Z) - : (v.Y < v.Z) - ? new Int3(v.Y, v.Z, v.X) - : new Int3(v.Z, v.Y, v.X); - } - } - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/IMesh.cs b/src/cs/vim/Vim.Format.Core/Geometry/IMesh.cs deleted file mode 100644 index f330d0e4..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/IMesh.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Vim.G3d; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - /// - /// This is the interface for triangle meshes. - /// - public interface IMesh : - IGeometryAttributes, - ITransformable3D - { - IArray Vertices { get; } - IArray Indices { get; } - IArray VertexColors { get; } - IArray VertexNormals { get; } - IArray VertexUvs { get; } - - IArray SubmeshMaterials { get; } - IArray SubmeshIndexOffsets { get; } - IArray SubmeshIndexCount { get; } - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/IScene.cs b/src/cs/vim/Vim.Format.Core/Geometry/IScene.cs deleted file mode 100644 index eb2e5821..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/IScene.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Collections.Generic; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - /// - /// An IScene is a generic representation of a 3D scene graph. - /// - public interface IScene - { - IArray Nodes { get; } - IArray Meshes { get; } - } - - /// - /// A node in a scene graph. - /// - public interface ISceneNode - { - int Id { get; } - IScene Scene { get; } - Matrix4x4 Transform { get; } - int MeshIndex { get; } - IMesh GetMesh(); - ISceneNode Parent { get; } - - // TODO: DEPRECATE: this needs to be removed, currently only used in Vim.Max.Bridge. - IArray Children { get; } - } - - public class SceneNodeComparer : EqualityComparer, IComparer - { - public static readonly SceneNodeComparer Instance = new SceneNodeComparer(); - - public int Compare(ISceneNode x, ISceneNode y) - => x.Id - y.Id; - public override bool Equals(ISceneNode x, ISceneNode y) - => x.Id == y.Id; - public override int GetHashCode(ISceneNode obj) - => obj.Id; - } - - public class NullNode : ISceneNode - { - public static NullNode Instance = new NullNode(); - public static List ListInstance = new List() { Instance }; - public int Id => -1; - public IScene Scene => null; - public Matrix4x4 Transform => Matrix4x4.Identity; - public int MeshIndex => 0; - public ISceneNode Parent => null; - public IArray Children => null; - public IMesh GetMesh() => null; - } - - public class IdNode : ISceneNode - { - public int Id { get; set; } - public IScene Scene => null; - public Matrix4x4 Transform => Matrix4x4.Identity; - public int MeshIndex => 0; - public ISceneNode Parent => null; - public IArray Children => null; - public IMesh GetMesh() => null; - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/MeshDebugView.cs b/src/cs/vim/Vim.Format.Core/Geometry/MeshDebugView.cs deleted file mode 100644 index 54a216e3..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/MeshDebugView.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - public class MeshDebugView - { - IMesh Interface { get; } - - public int NumCorners => Interface.NumCorners; - public int NumFaces => Interface.NumFaces; - public int NumMeshes => Interface.NumMeshes; - public int NumInstances => Interface.NumInstances; - - public Vector3[] Vertices => Interface.Vertices.ToArray(); - public int[] Indices => Interface.Indices.ToArray(); - - public MeshDebugView(IMesh g) - => Interface = g; - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/MeshExtensions.cs b/src/cs/vim/Vim.Format.Core/Geometry/MeshExtensions.cs deleted file mode 100644 index 506cb3d3..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/MeshExtensions.cs +++ /dev/null @@ -1,453 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Vim.G3d; -using Vim.LinqArray; -using Vim.Math3d; -using Vim.Util; - -namespace Vim.Format.Geometry -{ - public static class MeshExtensions - { - #region constructors - public static IMesh ToIMesh(this IArray self) - => self.ToEnumerable().ToIMesh(); - - public static IMesh ToIMesh(this IEnumerable self) - { - var tmp = new GeometryAttributes(self); - switch (tmp.NumCornersPerFace) - { - case 3: - return new TriMesh(tmp.Attributes.ToEnumerable()); - case 4: - return new QuadMesh(tmp.Attributes.ToEnumerable()).ToTriMesh(); - default: - throw new Exception($"Can not convert a geometry with {tmp.NumCornersPerFace} to a triangle mesh: only quad meshes"); - } - } - - public static IMesh ToIMesh(this IGeometryAttributes g) - => g is IMesh m ? m : g is QuadMesh q ? q.ToIMesh() : g.Attributes.ToIMesh(); - #endregion - - // Computes the topology: this is a slow O(N) operation - public static Topology ComputeTopology(this IMesh mesh) - => new Topology(mesh); - - public static double Area(this IMesh mesh) - => mesh.Triangles().Sum(t => t.Area); - - #region validation - public static bool IsDegenerateVertexIndices(this Int3 vertexIndices) - => vertexIndices.X == vertexIndices.Y || vertexIndices.X == vertexIndices.Z || vertexIndices.Y == vertexIndices.Z; - - public static bool HasDegenerateFaceVertexIndices(this IMesh self) - => self.AllFaceVertexIndices().Any(IsDegenerateVertexIndices); - #endregion - - // TODO: find a better location for this function. DotNetUtilties doesn't know about IArray unfortunately, so maybe this project needs its own Utility class. - public static DictionaryOfLists GroupBy(this IArray xs, Func groupingFunc) - { - var r = new DictionaryOfLists(); - for (var i = 0; i < xs.Count; ++i) - r.Add(groupingFunc(i), xs[i]); - return r; - } - - public static IArray GetFaceMaterials(this IMesh mesh) - { - // SubmeshIndexOffsets: [0, A, B] - // SubmeshIndexCount: [X, Y, Z] - // SubmeshMaterials: [L, M, N] - // --- - // FaceMaterials: [...Repeat(L, X / 3), ...Repeat(M, Y / 3), ...Repeat(N, Z / 3)] <-- divide by 3 for the number of corners per Triangular face - var numCornersPerFace = mesh.NumCornersPerFace; - return mesh.SubmeshIndexCount - .ToEnumerable() - .SelectMany((indexCount, i) => Enumerable.Repeat(mesh.SubmeshMaterials[i], indexCount / numCornersPerFace)) - .ToIArray(); - } - - public static IEnumerable DisctinctMaterials(this IMesh mesh) - => mesh.GetFaceMaterials().ToEnumerable().Distinct(); - - public static DictionaryOfLists IndicesByMaterial(this IMesh mesh) - { - var faceMaterials = mesh.GetFaceMaterials(); - return mesh.Indices.GroupBy(i => faceMaterials[i / 3]); - } - - public static IMesh Merge(this IArray meshes) - => meshes.Select(m => (IGeometryAttributes)m).Merge().ToIMesh(); - - public static IMesh Merge(this IEnumerable meshes) - => meshes.ToIArray().Merge(); - - public static IMesh Merge(this IMesh mesh, params IMesh[] others) - { - var gs = others.ToList(); - gs.Insert(0, mesh); - return gs.Merge(); - } - - public static IEnumerable<(int Material, IMesh Mesh)> SplitByMaterial(this IMesh mesh) - { - var submeshMaterials = mesh.SubmeshMaterials; - if (submeshMaterials == null || submeshMaterials.Count == 0) - { - // Base case: no submesh materials are defined on the mesh. - return new[] { (-1, mesh) }; - } - - var submeshIndexOffets = mesh.SubmeshIndexOffsets; - var submeshIndexCounts = mesh.SubmeshIndexCount; - if (submeshIndexOffets == null || submeshIndexCounts == null || - submeshMaterials.Count <= 1 || submeshIndexOffets.Count <= 1 || submeshIndexCounts.Count <= 1) - { - // Base case: only one submesh material. - return new [] { (submeshMaterials[0], mesh) }; - } - - // Example: - // - // ------------ - // INPUT MESH: - // ------------ - // Vertices [Va, Vb, Vc, Vd, Ve, Vf, Vg] <-- 7 vertices - // Indices [0 (Va), 1 (Vb), 2 (Vc), 1 (Vb), 2 (Vc), 3 (Vd), 4 (Ve), 5 (Vf), 6 (Vg)] <-- 3 triangles referencing the 7 vertices - // SubmeshIndexOffsets [0, 3, 6] - // SubmeshIndexCount [3, 3, 3] (computed) - // SubmeshMaterials [Ma, Mb, Mc] - // - // ------------ - // OUTPUT MESHES - // ------------ - // - MESH FOR MATERIAL Ma - // Vertices: [Va, Vb, Vc] - // Indices: [0, 1, 2] - // SubmeshIndexOffsets: [0] - // SubmeshMaterials: [Ma] - // - //- MESH FOR MATERIAL Mb - // Vertices: [Vb, Vc, Vd] - // Indices: [0, 1, 2] - // SubmeshIndexOffsets: [0] - // SubmeshMaterials: [Mb] - // - //- MESH FOR MATERIAL Mc - // Vertices: [Ve, Vf, Vg] - // Indices: [0, 1, 2] - // SubmeshIndexOffsets: [0] - // SubmeshMaterials: [Mc] - - return mesh.SubmeshMaterials - .Select((submeshMaterial, submeshIndex) => (submeshMaterial, submeshIndex)) - .GroupBy(t => t.submeshMaterial) - .SelectMany(g => - { - var material = g.Key; - var meshes = g.Select((t, _) => - { - var submeshMaterial = t.submeshMaterial; - var submeshStartIndex = submeshIndexOffets[t.submeshIndex]; - var submeshIndexCount = submeshIndexCounts[t.submeshIndex]; - - var indexSlice = mesh.Indices.Slice(submeshStartIndex, submeshStartIndex + submeshIndexCount); - - var newVertexAttributes = mesh.VertexAttributes().Select(attr => attr.Remap(indexSlice)); - var newIndexAttribute = indexSlice.Count.Select(i => i).ToIndexAttribute(); - - var newSubmeshIndexOffsets = 0.Repeat(1).ToSubmeshIndexOffsetAttribute(); - var newSubmeshMaterials = submeshMaterial.Repeat(1).ToSubmeshMaterialAttribute(); - - return newVertexAttributes - .Concat(mesh.NoneAttributes()) - .Concat(mesh.WholeGeometryAttributes()) - // TODO: TECH DEBT - face, edge, and corner attributes are ignored for now. - .Append(newIndexAttribute) - .Append(newSubmeshIndexOffsets) - .Append(newSubmeshMaterials) - .ToGeometryAttributes() - .ToIMesh(); - }); - - return meshes.Select(m => (material, m)); - }); - } - - public static IGeometryAttributes DeleteUnusedVertices(this IMesh mesh) - { - var tmp = new bool[mesh.Vertices.Count]; - for (var i = 0; i < mesh.Indices.Count; ++i) - tmp[mesh.Indices[i]] = true; - - var remap = new List(); - for (var i = 0; i < tmp.Length; ++i) - { - if (tmp[i]) - remap.Add(i); - } - - return mesh.RemapVertices(remap.ToIArray()); - } - - public static bool GeometryEquals(this IMesh mesh, IMesh other, float tolerance = Math3d.Constants.Tolerance) - { - if (mesh.NumFaces != other.NumFaces) - return false; - return mesh.Triangles().Zip(other.Triangles(), (t1, t2) => t1.AlmostEquals(t2, tolerance)).All(x => x); - } - - public static IMesh SimplePolygonTessellate(this IEnumerable points) - { - var pts = points.ToList(); - var cnt = pts.Count; - var sum = Vector3.Zero; - var idxs = new List(pts.Count * 3); - for (var i = 0; i < pts.Count; ++i) - { - idxs.Add(i); - idxs.Add(i + 1 % cnt); - idxs.Add(cnt); - sum += pts[i]; - } - - var midPoint = sum / pts.Count; - pts.Add(midPoint); - - return Primitives.TriMesh(pts.ToIArray(), idxs.ToIArray()); - } - - public static IGeometryAttributes ReverseWindingOrder(this IMesh mesh) - { - var n = mesh.Indices.Count; - var r = new int[n]; - for (var i = 0; i < n; i += 3) - { - r[i + 0] = mesh.Indices[i + 2]; - r[i + 1] = mesh.Indices[i + 1]; - r[i + 2] = mesh.Indices[i + 0]; - } - return mesh.SetAttribute(r.ToIArray().ToIndexAttribute()); - } - - /// - /// Returns the closest point in a sequence of points - /// - public static Vector3 NearestPoint(this IEnumerable points, Vector3 x) - => points.Minimize(float.MaxValue, p => p.DistanceSquared(x)); - - /// - /// Returns the closest point in a sequence of points - /// - public static Vector3 NearestPoint(this IArray points, Vector3 x) - => points.ToEnumerable().NearestPoint(x); - - /// - /// Returns the closest point in a geometry - /// - public static Vector3 NearestPoint(this IMesh mesh, Vector3 x) - => mesh.Vertices.NearestPoint(x); - - public static Vector3 FurthestPoint(this IMesh mesh, Vector3 x0, Vector3 x1) - => mesh.Vertices.FurthestPoint(x0, x1); - - public static Vector3 FurthestPoint(this IArray points, Vector3 x0, Vector3 x1) - => points.ToEnumerable().FurthestPoint(x0, x1); - - public static Vector3 FurthestPoint(this IEnumerable points, Vector3 x0, Vector3 x1) - => points.Maximize(float.MinValue, v => v.Distance(x0).Min(v.Distance(x1))); - - public static Vector3 FurthestPoint(this IMesh mesh, Vector3 x) - => mesh.Vertices.FurthestPoint(x); - - public static Vector3 FurthestPoint(this IArray points, Vector3 x) - => points.ToEnumerable().FurthestPoint(x); - - public static Vector3 FurthestPoint(this IEnumerable points, Vector3 x) - => points.Maximize(float.MinValue, v => v.Distance(x)); - - public static IGeometryAttributes SnapPoints(this IMesh mesh, float snapSize) - => snapSize.Abs() >= Math3d.Constants.Tolerance - ? mesh.Deform(v => (v * snapSize.Inverse()).Truncate() * snapSize) - : mesh.Deform(v => Vector3.Zero); - - /// - /// Returns the vertices organized by face corner. - /// - public static IArray VerticesByIndex(this IMesh mesh) - => mesh.Vertices.SelectByIndex(mesh.Indices); - - /// - /// Returns the vertices organized by face corner, normalized to the first position. - /// This is useful for detecting if two meshes are the same except offset by - /// position. - /// - public static IArray NormalizedVerticesByCorner(this IMesh m) - { - if (m.NumCorners == 0) - return Vector3.Zero.Repeat(0); - var firstVertex = m.Vertices[m.Indices[0]]; - return m.VerticesByIndex().Select(v => v - firstVertex); - } - - /// - /// Compares the face positions of two meshes normalized by the vertex buffer, returning the maximum distance, or null - /// if the meshes have different topology. - /// - public static float? MaxNormalizedDistance(this IMesh mesh, IMesh other) - { - var xs = mesh.NormalizedVerticesByCorner(); - var ys = other.NormalizedVerticesByCorner(); - if (xs.Count != ys.Count) - return null; - return xs.Zip(ys, (x, y) => x.Distance(y)).Max(); - } - - public static AABox BoundingBox(this IMesh mesh) - => AABox.Create(mesh.Vertices.ToEnumerable()); - - public static Sphere BoundingSphere(this IMesh mesh) - => mesh.BoundingBox().ToSphere(); - - public static float BoundingRadius(this IMesh mesh) - => mesh.BoundingSphere().Radius; - - public static Vector3 Center(this IMesh mesh) - => mesh.BoundingBox().Center; - - public static Vector3 Centroid(this IMesh mesh) - => mesh.Vertices.Aggregate(Vector3.Zero, (x, y) => x + y) / mesh.Vertices.Count; - - public static bool AreIndicesValid(this IMesh mesh) - => mesh.Indices.All(i => i >= 0 && i < mesh.Vertices.Count); - - public static bool AreAllVerticesUsed(this IMesh mesh) - { - var used = new bool[mesh.Vertices.Count]; - mesh.Indices.ForEach(idx => used[idx] = true); - return used.All(b => b); - } - - public static IMesh ResetPivot(this IMesh mesh) - => mesh.Translate(-mesh.BoundingBox().CenterBottom); - - #region Face operations - - /// - /// Given an array of face data, creates an array of indexed data to match vertices - /// - public static IArray FaceDataToVertexData(this IMesh mesh, IArray data) - { - if (data.Count != mesh.NumFaces) - throw new Exception("Cannot match input Face data to existing faces"); - - var vertexData = new T[mesh.NumVertices]; - for (var i = 0; i < mesh.Indices.Count; ++i) - vertexData[mesh.Indices[i]] = data[i / 3]; - return vertexData.ToIArray(); - } - - public static IArray AllFaceVertexIndices(this IMesh mesh) - => mesh.NumFaces.Select(mesh.FaceVertexIndices); - - public static Int3 FaceVertexIndices(this IMesh mesh, int faceIndex) - => new Int3(mesh.Indices[faceIndex * 3], mesh.Indices[faceIndex * 3 + 1], mesh.Indices[faceIndex * 3 + 2]); - - public static Triangle VertexIndicesToTriangle(this IMesh mesh, Int3 indices) - => new Triangle(mesh.Vertices[indices.X], mesh.Vertices[indices.Y], mesh.Vertices[indices.Z]); - - public static Triangle Triangle(this IMesh mesh, int face) - => mesh.VertexIndicesToTriangle(mesh.FaceVertexIndices(face)); - - public static IArray Triangles(this IMesh mesh) - => mesh.NumFaces.Select(mesh.Triangle); - - public static IArray GetAllEdgesAsLines(this IMesh mesh) - => mesh.Triangles().SelectMany(tri => Tuple.Create(tri.AB, tri.BC, tri.CA)); - - public static IArray ComputedNormals(this IMesh mesh) - => mesh.Triangles().Select(t => t.Normal); - - public static bool Planar(this IMesh mesh, float tolerance = Math3d.Constants.Tolerance) - { - if (mesh.NumFaces <= 1) return true; - var normal = mesh.Triangle(0).Normal; - return mesh.ComputedNormals().All(n => n.AlmostEquals(normal, tolerance)); - } - - public static IArray MidPoints(this IMesh mesh) - => mesh.Triangles().Select(t => t.MidPoint); - - public static IArray FacesToCorners(this IMesh mesh) - => mesh.NumFaces.Select(i => i * 3); - - public static IArray FaceDataToCornerData(this IMesh mesh, IArray data) - => mesh.NumCorners.Select(i => data[i / 3]); - - public static IArray GetOrComputeFaceNormals(this IMesh mesh) - => mesh.GetAttributeFaceNormal()?.Data ?? mesh.ComputedNormals(); - - public static IArray GetOrComputeVertexNormals(this IMesh mesh) - => mesh.VertexNormals ?? mesh.ComputeTopology().GetOrComputeVertexNormals(); - - /// - /// Returns vertex normals if present, otherwise computes vertex normals naively by averaging them. - /// Given a pre-computed topology, will-leverage that. - /// A more sophisticated algorithm would compute the weighted normal - /// based on an angle. - /// - public static IArray GetOrComputeVertexNormals(this Topology topo) - { - var mesh = topo.Mesh; - var r = mesh.VertexNormals; - if (r != null) return r; - var faceNormals = mesh.GetOrComputeFaceNormals().ToArray(); - return mesh - .NumVertices - .Select(vi => - { - var tmp = topo - .FacesFromVertexIndex(vi) - .Select(fi => faceNormals[fi]) - .Average(); - if (tmp.IsNaN()) - return Vector3.Zero; - return tmp.SafeNormalize(); - }); - } - - public static IMesh CopyFaces(this IMesh mesh, Func predicate) - => (mesh as IGeometryAttributes).CopyFaces(predicate).ToIMesh(); - - public static IMesh CopyFaces(this IMesh mesh, IArray keep) - => mesh.CopyFaces(i => keep[i]); - - public static IMesh CopyFaces(this IMesh mesh, IArray keep) - => mesh.RemapFaces(keep).ToIMesh(); - - public static IMesh DeleteFaces(this IMesh mesh, Func predicate) - => mesh.CopyFaces(f => !predicate(f)); - #endregion - - #region Corner extensions - /// - /// Given an array of data associated with corners, return an array of data associated with - /// vertices. If a vertex is not referenced, no data is returned. If a vertex is referenced - /// multiple times, the last reference is used. - /// TODO: supplement with a proper interpolation system. - /// - public static IArray CornerDataToVertexData(this IMesh mesh, IArray data) - { - var vertexData = new T[mesh.NumVertices]; - for (var i = 0; i < data.Count; ++i) - vertexData[mesh.Indices[i]] = data[i]; - return vertexData.ToIArray(); - } - #endregion - - - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs b/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs index 6efb92f3..4fdcfd5d 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs @@ -1,24 +1,21 @@ using System.Collections.Generic; -using System.Diagnostics; using System.Linq; -using System.Runtime.CompilerServices; -using Vim.G3d; -using Vim.LinqArray; using Vim.Math3d; namespace Vim.Format.Geometry { + /// /// This class is used to compare quickly two meshes within a lookup table (e.g. Dictionary, HashTable). /// it looks at the positions of each corner, and the number of faces, and assures that the object ID /// and material IDs are the same. /// When using a class within a dictionary or hash table, the equals operator is called frequently. - /// By converting an IMesh to a MeshHash we minimize the amount of comparisons done. It becomes + /// By converting an VimMesh to a MeshHash we minimize the amount of comparisons done. It becomes /// possible, but highly unlikely that two different meshes would have the same hash. /// public class MeshHash { - public IMesh Mesh; + public VimMesh Mesh; public float Tolerance; public int NumFaces; public int NumVertices; @@ -26,19 +23,19 @@ public class MeshHash public Int3 BoxExtents; public Int3 BoxMin; - public int Round(float f) + private int Round(float f) => (int)(f / Tolerance); - public Int3 Round(Vector3 v) + private Int3 Round(Vector3 v) => new Int3(Round(v.X), Round(v.Y), Round(v.Z)); - public MeshHash(IMesh mesh, float tolerance) + public MeshHash(VimMesh mesh, float tolerance) { Mesh = mesh; Tolerance = tolerance; NumFaces = mesh.NumFaces; NumVertices = mesh.NumVertices; - TopologyHash = Hash.Combine(mesh.Indices.ToArray()); + TopologyHash = Hash.Combine(mesh.indices); var box = mesh.BoundingBox(); BoxMin = Round(box.Min); BoxExtents = Round(box.Extent); @@ -47,7 +44,7 @@ public MeshHash(IMesh mesh, float tolerance) public override bool Equals(object obj) => obj is MeshHash other && Equals(other); - public bool Equals(MeshHash other) + private bool Equals(MeshHash other) => NumFaces == other.NumFaces && NumVertices == other.NumVertices && BoxMin.Equals(other.BoxMin) @@ -57,88 +54,4 @@ public bool Equals(MeshHash other) public override int GetHashCode() => Hash.Combine(NumFaces, NumVertices, TopologyHash, BoxMin.GetHashCode(), BoxExtents.GetHashCode()); } - - public class IntegerPositionColorNormal - { - public Int3 Position; - public Int3 Color; - public Int3 Normal; - - public IntegerPositionColorNormal(Int3 pos, Int3 color, Int3 normal) - => (Position, Color, Normal) = (pos, color, normal); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - => Hash.Combine(Position.GetHashCode(), Color.GetHashCode(), Normal.GetHashCode()); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) - => Equals(obj as IntegerPositionColorNormal); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(IntegerPositionColorNormal other) - => other != null && Position.Equals(other.Position) && Color.Equals(other.Color) && Normal.Equals(other.Normal); - } - - public static class Optimization - { - public static Dictionary> GroupMeshesByHash(this IArray meshes, float tolerance) - => meshes.ToEnumerable().GroupMeshesByHash(tolerance); - - public static Dictionary> GroupMeshesByHash(this IEnumerable meshes, float tolerance) - => meshes.AsParallel().GroupBy(m => new MeshHash(m, tolerance)).ToDictionary(grp => grp.Key, grp => grp.ToList()); - - /// - /// Merges vertices that are within a certain distance and have similar normals and colors. - /// - public static IMesh WeldVertices(this IMesh g, float threshold = (float)Math3d.Constants.MmToFeet) - { - var positions = g.Vertices; - var normals = g.GetOrComputeVertexNormals().ToArray(); - var colors = g.VertexColors ?? Vector4.Zero.Repeat(positions.Count); - - // Vertex indices by color, and then by normal - var d = new Dictionary(); - - // The mapping of old indices to new ones - var indexRemap = new int[g.Vertices.Count]; - - // This is a list of vertex indices that we are keeping - var vertRemap = new List(); - - // Local helper function - Int3 ToInt3(Vector3 v) - => new Int3((int)v.X, (int)v.Y, (int)v.Z); - - for (var i = 0; i < g.NumVertices; ++i) - { - var p = ToInt3(positions[i] * (1 / threshold)); - var c = ToInt3(colors[i].ToVector3() * 10000); - var n = ToInt3(normals[i] * 10000); - - var pcn = new IntegerPositionColorNormal(p, c, n); - - if (d.TryGetValue(pcn, out var index)) - { - indexRemap[i] = index; - continue; - } - - var newVertIndex = vertRemap.Count; - indexRemap[i] = newVertIndex; - vertRemap.Add(i); - - d.Add(pcn, newVertIndex); - } - - Debug.Assert(vertRemap.Count <= g.NumVertices); - for (var i = 1; i < vertRemap.Count; ++i) - Debug.Assert(vertRemap[i - 1] < vertRemap[i]); - - return g.RemapVertices( - vertRemap.ToIArray(), - g.Indices.Select(i => indexRemap[i])) - .ToIMesh(); - } - } } diff --git a/src/cs/vim/Vim.Format.Core/Geometry/PerimeterProjection.cs b/src/cs/vim/Vim.Format.Core/Geometry/PerimeterProjection.cs deleted file mode 100644 index 854e7292..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/PerimeterProjection.cs +++ /dev/null @@ -1,657 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Vim.Util; -using Vim.LinqArray; -using Vim.Math3d; -using LineApprox = System.Tuple; - -namespace Vim.Format.Geometry -{ - public static class PerimeterProjection - { - public static Int2 ToXYApproximation(this Vector3 v, float tolerance) - => new Int2((int)(v.X / tolerance), (int)(v.Y / tolerance)); - - public static Vector2 FromXYApproximation(this Int2 v, float tolerance) - => new Vector2(v.X * tolerance, v.Y * tolerance); - - public static LineApprox ToXYApproximation(this Line line, float tolerance) - => Tuple.Create(line.A.ToXYApproximation(tolerance), line.B.ToXYApproximation(tolerance)); - - public static double Angle(this Int2 v) - => Math.Atan2(v.Y, v.X); - - public static double Angle(this Int2 a, Int2 b) - => (a - b).Angle(); - - public static IEnumerable PerimeterXY(this IMesh mesh, float tolerance = 0.001f) - { - var srcLines = mesh.GetAllEdgesAsLines(); - var approxLines = srcLines.Select(line => line.ToXYApproximation(tolerance)); - var lineSet = new HashSet(approxLines.ToArrayInParallel()); - Debug.WriteLine($"Went from {srcLines.Count} to {lineSet.Count}"); - var d = new DictionaryOfLists(); - foreach (var ab in lineSet) - { - d.Add(ab.Item1, ab); - d.Add(ab.Item2, ab); - } - var r = new List(); - if (d.Count == 0) - return r; - - var firstKey = d.Keys.Minimize(int.MaxValue, ab => ab.X.Min(ab.Y)); - var currentKey = firstKey; - var prevAngle = 0.0; - - // If we can't find the point in the dictionary we have completed - while (d.ContainsKey(currentKey)) - { - // Find the candidate points; - var candidates = d[currentKey].Select(line => line.Item1 == currentKey ? line.Item2 : line.Item1); - - // Find the best match by maximizing angle - var bestMatch = candidates.Maximize(0.0, c => currentKey.Angle(c) - prevAngle); - - // Update the return set - r.Add(bestMatch.FromXYApproximation(tolerance)); - - // Now save the angle for the next stage. - prevAngle = currentKey.Angle(bestMatch); - - // Remove this key from the dictionary - d.Remove(currentKey); - - // Now we are at a new point - currentKey = bestMatch; - } - - return r; - } - - public static List> GeneratePerimeter(this IMesh mesh, Vector3 planeNormal, float degenerateSegmentEpsilon = 10.0f, float edgeLoopThreshold = 1e-6f) - { - var q = GetClusterRotation(planeNormal.Normalize(), Vector3.UnitZ); - - var segments = mesh.BuildBoundarySegments(null, - input => input.Transform(q).ToVector2()); - - segments = segments.SplitIntersectingCurves(edgeLoopThreshold * 50); - - var graph = segments.BuildConnectionGraph(edgeLoopThreshold * 50); - var loops = graph.FindGraphLoops(edgeLoopThreshold * 50); - - loops = loops.RemoveDuplicatedVertices(edgeLoopThreshold * 50); - - while (true) - { - var newLoops = loops.RemoveDegenerateSegmentsLooping(edgeLoopThreshold); - if (newLoops.Count == loops.Count) - { - var notEqual = false; - for (var i = 0; i < newLoops.Count; i++) - { - if (newLoops[i].Count != loops[i].Count) - { - notEqual = true; - break; - } - } - - if (!notEqual) - { - break; - } - } - - loops = newLoops; - } - - return loops; - } - - public static bool IsNaN(this Quaternion q) - => q.X.IsNaN() || q.Y.IsNaN() || q.Z.IsNaN() || q.W.IsNaN(); - - // Finds all of the intersections between the curves and splits them - // Also adds a pseudo intersection at the left-most point, this gives the FindGraphLoops - // algorithm a good starting point - public static List> SplitIntersectingCurves(this List> curves, float threshold) - { - var thresholdSquared = threshold * threshold; - var curveList = new LinkedList>(curves); - - var intersections = new Dictionary, List>>>(); // Madness in great ones must not unwatched go. - var leftMostPoint = Vector2.MaxValue; - List leftMostCurve = null; - var leftMostIntersectionPoint = 0.0f; - - foreach (var currentCurveA in curveList) - { - for (var i = 0; i < currentCurveA.Count; i++) - { - var point = currentCurveA[i]; - if (point.X < leftMostPoint.X || (point.X == leftMostPoint.X && point.Y < leftMostPoint.Y)) - { - leftMostPoint = point; - leftMostIntersectionPoint = i; - leftMostCurve = currentCurveA; - } - } - - foreach (var currentCurveB in curveList) - { - if (currentCurveA != currentCurveB) - { - float t, u; - var distance = IntersectCurves(currentCurveA, currentCurveB, out t, out u); - if (distance < threshold) - { - if (!intersections.ContainsKey(currentCurveA)) - { - intersections[currentCurveA] = new List>>(); - } - if (!intersections.ContainsKey(currentCurveB)) - { - intersections[currentCurveB] = new List>>(); - } - - intersections[currentCurveA].Add(new Tuple>(distance, t, currentCurveB)); - intersections[currentCurveB].Add(new Tuple>(distance, u, currentCurveA)); - } - } - } - } - - // split the left most curve so that FindGraphLoops has a good starting point - if (leftMostIntersectionPoint != 0.0 && leftMostIntersectionPoint != leftMostCurve.Count - 1) - { - intersections[leftMostCurve].Add(new Tuple>(0.0f, leftMostIntersectionPoint, null)); - } - - var edgeLoops = new List>(); - foreach (var intersection in intersections) - { - var splits = intersection.Value.Select(x => x.Item2); - - edgeLoops.AddRange(intersection.Key.SplitCurve(splits)); - } - - return edgeLoops; - } - - // Cuts the curve between min and max and returns the part in between - public static List> SplitCurve(this List curve, IEnumerable splits) - { - var result = new List>(); - if (splits.Count() == 0) - { - result.Add(curve); - return result; - } - - splits = splits.OrderBy(x => x).ToList(); - var newSplits = new List(); - if (splits.First() != 0.0f) - { - newSplits.Add(0.0f); - } - - newSplits.AddRange(splits); - - if (splits.Last() < curve.Count - 1) - { - newSplits.Add(curve.Count - 1); - } - - for (var i = 0; i < newSplits.Count - 1; i++) - { - var min = newSplits[i]; - var max = newSplits[i + 1]; - - var clampedCurve = curve.ClampCurve(min, max); - - if (clampedCurve.CurveLength() > 1e-5f) - { - result.Add(clampedCurve); - } - } - - return result; - } - - - // Cuts the curve between min and max and returns the part in between - public static List ClampCurve(this List curve, float min, float max) - { - var floorMin = (float)Math.Floor(min); - var floorMax = (float)Math.Floor(max); - var fracMin = min - floorMin; - var fracMax = max - floorMax; - - if (fracMin == 0.0 && floorMin > 0) - { - floorMin--; - fracMin++; - } - if (fracMax == 0.0 && floorMax > 0) - { - floorMax--; - fracMax++; - } - - var indexMin = (int)floorMin; - var indexMax = (int)floorMax; - - var result = new List(); - if (fracMin != 1.0) - { - result.Add(curve[indexMin].Lerp(curve[indexMin + 1], (float)fracMin)); - } - for (var i = indexMin + 1; i <= indexMax; i++) - { - result.Add(curve[i]); - } - result.Add(curve[indexMax].Lerp(curve[indexMax + 1], (float)fracMax)); - - return result; - } - - // Returns the length of the curve - public static float CurveLength(this List curve) - { - var curveLength = 0.0f; - for (var x = 0; x < curve.Count - 1; x++) - { - curveLength += (curve[x] - curve[x + 1]).Length(); - } - - return curveLength; - } - - // Returns min distance between piecewise linear curves A and B. - // t = distance along curve A - // u = distance along curve B - // TODO: Return multiple intersections within a tolerance - public static float IntersectCurves(List segmentsA, List segmentsB, out float t, out float u) - { - var minDistance = float.MaxValue; - t = 0.0f; - u = 0.0f; - - for (var segmentAIndex = 0; segmentAIndex < segmentsA.Count - 1; segmentAIndex++) - { - var v1a = segmentsA[segmentAIndex]; - var v1b = segmentsA[segmentAIndex + 1]; - - for (var segmentBIndex = 0; segmentBIndex < segmentsB.Count - 1; segmentBIndex++) - { - var v2a = segmentsB[segmentBIndex]; - var v2b = segmentsB[segmentBIndex + 1]; - - float fracT, fracU; - var distance = GeometryCuttingUtils.LineLineDistance(v1a, v1b, v2a, v2b, out fracT, out fracU); - - if (distance < minDistance) - { - minDistance = distance; - t = segmentAIndex + fracT; - u = segmentBIndex + fracU; - } - } - } - - return minDistance; - } - - public static List> RemoveDuplicatedVertices(this List> edgeLoops, float threshold) - { - var thresholdSquared = threshold * threshold; - foreach (var edgeLoop in edgeLoops) - { - for (var i = 0; i < edgeLoop.Count; i++) - { - if ((edgeLoop[i] - edgeLoop[(i + 1) % edgeLoop.Count]).LengthSquared() <= thresholdSquared) - { - edgeLoop.RemoveAt(i); - i--; - } - } - } - - return edgeLoops; - } - - public static Quaternion GetClusterRotation(Vector3 clusterNormal, Vector3 alignTo, float tolerance = 0.001f) - { - var c = clusterNormal.Cross(alignTo); - var d = clusterNormal.Dot(alignTo); - - if (d >= 1f - tolerance) - return Quaternion.Identity; - - if (d <= -(1f - tolerance)) - { - // Need an axis of rotation that is perpendicular to the two input vectors - var axis = clusterNormal.Cross(alignTo.Dot(Vector3.UnitY) > 0.999f ? Vector3.UnitZ : Vector3.UnitY); - return Quaternion.CreateFromAxisAngle(axis, (float)Math.PI); - } - - var s = (float)Math.Sqrt((1.0f + d) * 2.0f); - var invs = 1.0f / s; - - var q = new Quaternion(c * invs, s * 0.5f); - q = q.Normalize(); - - Debug.Assert(!q.IsNaN()); - return q; - } - - public static void AddEdgeToDictionary(Dictionary, int> d, int x, int y) - { - var key = new Tuple(x, y); - if (!d.ContainsKey(key)) - d[key] = 1; - else - d[key]++; - } - - private class EdgeEqualityComparer : IEqualityComparer> - { - public bool Equals(Tuple x, Tuple y) - => ((x.Item1 == y.Item1) && (x.Item2 == y.Item2)) || ((x.Item1 == y.Item2) && (x.Item2 == y.Item1)); - - public int GetHashCode(Tuple obj) - => obj.Item1.GetHashCode() ^ obj.Item2.GetHashCode(); - } - - public static Dictionary, int> BuildEdgeDictionary(this IMesh mesh) - { - var edges = new Dictionary, int>(new EdgeEqualityComparer()); - - var indices = mesh.Indices; - - for (var i = 0; i < indices.Count; i += 3) - { - var i0 = indices[i + 0]; - var i1 = indices[i + 1]; - var i2 = indices[i + 2]; - - AddEdgeToDictionary(edges, i0, i1); - AddEdgeToDictionary(edges, i1, i2); - AddEdgeToDictionary(edges, i2, i0); - } - - return edges; - } - - public static List> BuildBoundarySegments(this IMesh mesh, Dictionary, int> edgeDictionary, Func transform) - { - var segments = new List>(); - - var indices = mesh.Indices; - var vertices = mesh.Vertices; - - for (var i = 0; i < indices.Count; i += 3) - { - var i0 = indices[i + 0]; - var i1 = indices[i + 1]; - var i2 = indices[i + 2]; - - var v0 = transform(vertices[i0]); - var v1 = transform(vertices[i1]); - var v2 = transform(vertices[i2]); - - if (edgeDictionary == null || edgeDictionary[new Tuple(i0, i1)] != 2) segments.Add(new List { v0, v1 }); - if (edgeDictionary == null || edgeDictionary[new Tuple(i1, i2)] != 2) segments.Add(new List { v1, v2 }); - if (edgeDictionary == null || edgeDictionary[new Tuple(i2, i0)] != 2) segments.Add(new List { v2, v0 }); - } - - return segments; - } - - // Builds a node connection graph from a list of curves - public static Dictionary, List>>> BuildConnectionGraph(this List> curves, float threshold) - { - var connectionGraph = new Dictionary, List>>>(); - var reverseCurves = new Dictionary, List>(); - var thresholdSquared = threshold * threshold; - - var pointList = new Vector2[curves.Count]; - for (var i = 0; i < curves.Count; i++) - { - var curve = curves[i]; - connectionGraph[curve.First()] = new List, List>>(); - connectionGraph[curve.Last()] = new List, List>>(); - - var reverseCurve = new List(curve); - reverseCurve.Reverse(); - reverseCurves[curve] = reverseCurve; - reverseCurves[reverseCurve] = curve; - } - - foreach (var point in connectionGraph) - { - foreach (var curve in curves) - { - var distanceSquared = (curve.First() - point.Key).LengthSquared(); - if (distanceSquared <= thresholdSquared) - { - point.Value.Add(new Tuple, List>(curve, reverseCurves[curve])); - } - - distanceSquared = (curve.Last() - point.Key).LengthSquared(); - if (distanceSquared <= thresholdSquared) - { - point.Value.Add(new Tuple, List>(reverseCurves[curve], curve)); - } - } - } - - return connectionGraph; - } - - // Build a loop around the perimeter of the graph - // TODO: Handle multiple loops - public static List> FindGraphLoops(this Dictionary, List>>> connectionGraph, float threshold) - { - // Start from the left most point, build the perimeter by selecting the connection with the minimum clockwise angle - var thresholdSquared = threshold * threshold; - var leftMostPoint = Vector2.MaxValue; - foreach (var item in connectionGraph) - { - if (item.Value.Count < 2) - { - continue; - } - if (item.Key.X < leftMostPoint.X || (item.Key.X == leftMostPoint.X && item.Key.Y < leftMostPoint.Y)) - { - leftMostPoint = item.Key; - } - } - - var visitedNodes = new Dictionary, bool>(); - - var result = new List>(); - var loop = new List(); - var currentPointLast = leftMostPoint + new Vector2(0, 1); - var currentPoint = leftMostPoint; - var previousPoint = currentPoint; - - var segmentCount = 0; - while (true) - { - if (!connectionGraph.ContainsKey(currentPoint)) - { - // Failed :( - result.Add(loop); - return result; - } - - var list = connectionGraph[currentPoint].Where(x => - { - if (connectionGraph[x.Item1.Last()].Count <= 1) - { - // Don't go down a dead end, this might need improving with some kind of backtracking when a dead end was found - return false; - } - - if (visitedNodes.ContainsKey(x.Item1)) - { - return false; - } - - return true; - }); - var sortedList = list.OrderBy(x => - { - var s1 = (currentPoint - currentPointLast).Normalize(); - var s2 = (x.Item1[1] - x.Item1[0]).Normalize(); - var angle = (float)Math.Atan2(s1.Cross(s2), s1.Dot(s2)); - return angle; - }).ToList(); - - if (sortedList.Count == 0) - { - // Failed :( - result.Add(loop); - return result; - } - - var nextConnection = sortedList.First(); - visitedNodes[nextConnection.Item1] = true; - visitedNodes[nextConnection.Item2] = true; - segmentCount++; - loop.AddRange(nextConnection.Item1); - previousPoint = currentPoint; - currentPoint = nextConnection.Item1.Last(); - currentPointLast = nextConnection.Item1[nextConnection.Item1.Count - 2]; - - if ((currentPoint - loop[0]).LengthSquared() < thresholdSquared) - { - result.Add(loop); - break; - } - - if (segmentCount > connectionGraph.Count) - { - // Failed :( - result.Add(loop); - return result; - } - } - - return result; - } - - // Remove degenerate segments - public static List> RemoveDegenerateSegments(this List> edgeLoops, float epsilon = 1e-10f) - { - var result = new List>(); - foreach (var edgeLoop in edgeLoops) - { - if (edgeLoop.Count < 2) - { - //result.Add(edgeLoop); - continue; - } - - var newEdgeLoop = new List(edgeLoop); - for (var i = 1; i < newEdgeLoop.Count - 1; i++) - { - var v0 = newEdgeLoop[i - 1]; - var v1 = newEdgeLoop[i]; - var v2 = newEdgeLoop[i + 1]; - var area = (v1 - v0).Cross(v1 - v2).Abs(); - if (area < epsilon) - { - newEdgeLoop.RemoveAt(i); - i--; - } - } - - if (newEdgeLoop.Count < 2) - { - continue; - } - - result.Add(newEdgeLoop); - } - - return result; - } - public static List> RemoveDegenerateSegmentsLooping(this List> edgeLoops, float epsilon = 1e-10f) - { - var result = new List>(); - foreach (var edgeLoop in edgeLoops) - { - if (edgeLoop.Count < 2) - { - //result.Add(edgeLoop); - continue; - } - - var newEdgeLoop = new List(edgeLoop); - for (var i = 0; i < newEdgeLoop.Count; i++) - { - var v0 = newEdgeLoop[(i - 1 + newEdgeLoop.Count) % newEdgeLoop.Count]; - var v1 = newEdgeLoop[i]; - var v2 = newEdgeLoop[(i + 1) % newEdgeLoop.Count]; - var area = (v1 - v0).Cross(v1 - v2).Abs(); - if (area < epsilon) - { - newEdgeLoop.RemoveAt(i); - i--; - } - } - - if (newEdgeLoop.Count < 2) - { - continue; - } - - result.Add(newEdgeLoop); - } - - return result; - } - - public static List> RemoveDegenerateSegments(this List> edgeLoops, float epsilon, Func faceFunction) - { - var result = new List>(); - foreach (var edgeLoop in edgeLoops) - { - if (edgeLoop.Count < 2) - { - //result.Add(edgeLoop); - continue; - } - - var newEdgeLoop = new List(edgeLoop); - for (var i = 1; i < newEdgeLoop.Count - 1; i++) - { - var v0 = faceFunction(newEdgeLoop[i - 1]); - var v1 = faceFunction(newEdgeLoop[i]); - var v2 = faceFunction(newEdgeLoop[i + 1]); - var area = (v1 - v0).Cross(v1 - v2).LengthSquared(); - if (area < epsilon * epsilon) - { - newEdgeLoop.RemoveAt(i); - i--; - } - } - - if (newEdgeLoop.Count < 2) - { - continue; - } - - result.Add(newEdgeLoop); - } - - return result; - } - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs b/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs index 2e41024c..07755c32 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Vim.G3d; -using Vim.LinqArray; using Vim.Math3d; namespace Vim.Format.Geometry @@ -10,130 +8,47 @@ namespace Vim.Format.Geometry // TODO: plane, cylinder, cone, ruled face, public static class Primitives { - public static IMesh TriMesh(IEnumerable attributes) - => attributes.Where(x => x != null).ToIMesh(); - - public static IMesh TriMesh(params GeometryAttribute[] attributes) - => TriMesh(attributes.AsEnumerable()); - - public static IMesh TriMesh( - this IArray vertices, - IArray indices = null, - IArray uvs = null, - IArray colors = null, - IArray materials = null, - IArray submeshMaterials = null) - => TriMesh( - vertices?.ToPositionAttribute(), - indices?.ToIndexAttribute(), - uvs?.ToVertexUvAttribute(), - materials?.ToFaceMaterialAttribute(), - colors?.ToVertexColorAttribute(), - submeshMaterials?.ToSubmeshMaterialAttribute() - ); - - public static IMesh TriMesh(this IArray vertices, IArray indices = null, params GeometryAttribute[] attributes) - => new GeometryAttribute[] { - vertices?.ToPositionAttribute(), - indices?.ToIndexAttribute(), - }.Concat(attributes).ToIMesh(); - - public static IMesh QuadMesh(params GeometryAttribute[] attributes) - => QuadMesh(attributes.AsEnumerable()); - - public static IMesh QuadMesh(this IEnumerable attributes) - => new QuadMesh(attributes.Where(x => x != null)).ToTriMesh(); - - public static IMesh QuadMesh(this IArray vertices, IArray indices = null, IArray uvs = null, IArray materials = null, IArray objectIds = null) - => QuadMesh( - vertices.ToPositionAttribute(), - indices?.ToIndexAttribute(), - uvs?.ToVertexUvAttribute(), - materials?.ToFaceMaterialAttribute() - ); - - public static IMesh QuadMesh(Vector3 a, Vector3 b, Vector3 c, Vector3 d) - => QuadMesh(new[] { a, b, c, d }.ToIArray()); - - public static IMesh Cube - { - get - { - var vertices = new[] { - // front - new Vector3(-0.5f, -0.5f, 0.5f), - new Vector3(0.5f, -0.5f, 0.5f), - new Vector3(0.5f, 0.5f, 0.5f), - new Vector3(-0.5f, 0.5f, 0.5f), - // back - new Vector3(-0.5f, -0.5f, -0.5f), - new Vector3(0.5f, -0.5f, -0.5f), - new Vector3(0.5f, 0.5f, -0.5f), - new Vector3(-0.5f, 0.5f, -0.5f) - }.ToIArray(); - - var indices = new[] { - // front - 0, 1, 2, - 2, 3, 0, - // right - 1, 5, 6, - 6, 2, 1, - // back - 7, 6, 5, - 5, 4, 7, - // left - 4, 0, 3, - 3, 7, 4, - // bottom - 4, 5, 1, - 1, 0, 4, - // top - 3, 2, 6, - 6, 7, 3 - }.ToIArray(); - - return vertices.TriMesh(indices); - } - } - - public static IMesh CubeFaceted + public static VimMesh CreateCube() { - get - { - var cube = Cube; - return cube.Indices.Select(i => cube.Vertices[i]).TriMesh(cube.Indices.Count.Range()); - } + var vertices = new[] { + // front + new Vector3(-0.5f, -0.5f, 0.5f), + new Vector3(0.5f, -0.5f, 0.5f), + new Vector3(0.5f, 0.5f, 0.5f), + new Vector3(-0.5f, 0.5f, 0.5f), + // back + new Vector3(-0.5f, -0.5f, -0.5f), + new Vector3(0.5f, -0.5f, -0.5f), + new Vector3(0.5f, 0.5f, -0.5f), + new Vector3(-0.5f, 0.5f, -0.5f) + }; + + var indices = new[] { + // front + 0, 1, 2, + 2, 3, 0, + // right + 1, 5, 6, + 6, 2, 1, + // back + 7, 6, 5, + 5, 4, 7, + // left + 4, 0, 3, + 3, 7, 4, + // bottom + 4, 5, 1, + 1, 0, 4, + // top + 3, 2, 6, + 6, 7, 3 + }; + + return new VimMesh(indices, vertices); } - public static IMesh ToIMesh(this AABox box) - => Cube.Scale(box.Extent).Translate(box.Center); - - public static float Sqrt2 = 2.0f.Sqrt(); - - public static readonly IMesh Tetrahedron - = TriMesh(LinqArray.LinqArray.Create( - new Vector3(1f, 0.0f, -1f / Sqrt2), - new Vector3(-1f, 0.0f, -1f / Sqrt2), - new Vector3(0.0f, 1f, 1f / Sqrt2), - new Vector3(0.0f, -1f, 1f / Sqrt2)), - LinqArray.LinqArray.Create(0, 1, 2, 1, 0, 3, 0, 2, 3, 1, 3, 2)); - - public static readonly IMesh Square - = LinqArray.LinqArray.Create( - new Vector2(-0.5f, -0.5f), - new Vector2(-0.5f, 0.5f), - new Vector2(0.5f, 0.5f), - new Vector2(0.5f, -0.5f)).Select(x => x.ToVector3()).QuadMesh(); - - public static readonly IMesh Octahedron - = Square.Vertices.Append(Vector3.UnitZ, -Vector3.UnitZ).Normalize().TriMesh( - LinqArray.LinqArray.Create( - 0, 1, 4, 1, 2, 4, 2, 3, 4, - 3, 2, 5, 2, 1, 5, 1, 0, 5)); - // see: https://github.com/mrdoob/three.js/blob/9ef27d1af7809fa4d9943f8d4c4644e365ab6d2d/src/geometries/TorusBufferGeometry.js#L52 - public static Vector3 TorusFunction(Vector2 uv, float radius, float tube) + private static Vector3 TorusFunction(Vector2 uv, float radius, float tube) { uv *= Math3d.Constants.TwoPi; return new Vector3( @@ -142,43 +57,13 @@ public static Vector3 TorusFunction(Vector2 uv, float radius, float tube) tube * uv.Y.Sin()); } - public static IMesh Torus(float radius, float tubeRadius, int uSegs, int vSegs) + public static VimMesh Torus(float radius, float tubeRadius, int uSegs, int vSegs) => QuadMesh(uv => TorusFunction(uv, radius, tubeRadius), uSegs, vSegs); - // see: https://github.com/mrdoob/three.js/blob/9ef27d1af7809fa4d9943f8d4c4644e365ab6d2d/src/geometries/SphereBufferGeometry.js#L76 - public static Vector3 SphereFunction(Vector2 uv, float radius) - => new Vector3( - (float)(-radius * Math.Cos(uv.X * Math3d.Constants.TwoPi) * Math.Sin(uv.Y * Math3d.Constants.Pi)), - (float)(radius * Math.Cos(uv.Y * Math3d.Constants.Pi)), - (float)(radius * Math.Sin(uv.X * Math3d.Constants.TwoPi) * Math.Sin(uv.Y * Math3d.Constants.Pi))); - - public static IMesh Sphere(float radius, int uSegs, int vSegs) - => QuadMesh(uv => SphereFunction(uv, radius), uSegs, vSegs); - - /// - /// Creates a TriMesh from four points. - /// - public static IMesh TriMeshFromQuad(Vector3 a, Vector3 b, Vector3 c, Vector3 d) - => TriMesh(new[] { a, b, c, c, d, a }.ToIArray()); - - // Icosahedron, Dodecahedron, - - /// - /// Returns a collection of circular points. - /// - public static IArray CirclePoints(float radius, int numPoints) - => CirclePoints(numPoints).Select(x => x * radius); - - public static IArray CirclePoints(int numPoints) - => numPoints.Select(i => CirclePoint(i, numPoints)); - - public static Vector2 CirclePoint(int i, int numPoints) - => new Vector2((i * (Math3d.Constants.TwoPi / numPoints)).Cos(), (i * (Math3d.Constants.TwoPi / numPoints)).Sin()); - /// /// Computes the indices of a quad mesh astrip. /// - public static IArray ComputeQuadMeshStripIndices(int usegs, int vsegs, bool wrapUSegs = false, bool wrapVSegs = false) + private static int[] ComputeQuadMeshStripIndices(int usegs, int vsegs, bool wrapUSegs = false, bool wrapVSegs = false) { var indices = new List(); @@ -202,14 +87,14 @@ public static IArray ComputeQuadMeshStripIndices(int usegs, int vsegs, bool } } - return indices.ToIArray(); + return indices.ToArray(); } /// /// Returns the index buffer of a quad mesh strip. /// Returns an empty array if either numRowPoints or numPointsPerRow is less than 2. /// - public static List QuadMeshStripIndicesFromPointRows( + public static int[] QuadMeshStripIndicesFromPointRows( int numPointRows, int numPointsPerRow, bool clockwise = false) @@ -265,7 +150,7 @@ public static List QuadMeshStripIndicesFromPointRows( } } - return indices; + return indices.ToArray(); } public static int[] TriMeshCylinderCapIndices(int numEdgeVertices) @@ -314,13 +199,7 @@ public static int[] TriMeshCylinderCapIndices(int numEdgeVertices) /// /// Creates a quad mesh given a mapping from 2 space to 3 space /// - public static IMesh QuadMesh(this Func f, int segs) - => QuadMesh(f, segs, segs); - - /// - /// Creates a quad mesh given a mapping from 2 space to 3 space - /// - public static IMesh QuadMesh(this Func f, int usegs, int vsegs, bool wrapUSegs = false, bool wrapVSegs = false) + public static VimMesh QuadMesh(this Func f, int usegs, int vsegs, bool wrapUSegs = false, bool wrapVSegs = false) { var verts = new List(); var maxUSegs = wrapUSegs ? usegs : usegs + 1; @@ -335,23 +214,9 @@ public static IMesh QuadMesh(this Func f, int usegs, int vsegs verts.Add(f(new Vector2(u, v))); } } + var indices = ComputeQuadMeshStripIndices(usegs, vsegs, wrapUSegs, wrapVSegs); - return QuadMesh(verts.ToIArray(), ComputeQuadMeshStripIndices(usegs, vsegs, wrapUSegs, wrapVSegs)); - } - - /// - /// Creates a revolved face ... note that the last points are on top of the original - /// - public static IMesh RevolveAroundAxis(this IArray points, Vector3 axis, int segments = 4) - { - var verts = new List(); - for (var i = 0; i < segments; ++i) - { - var angle = Math3d.Constants.TwoPi / segments; - points.Rotate(axis, angle).AddTo(verts); - } - - return QuadMesh(verts.ToIArray(), ComputeQuadMeshStripIndices(segments - 1, points.Count - 1)); + return VimMesh.FromQuad(indices, verts.ToArray()); } } } diff --git a/src/cs/vim/Vim.Format.Core/Geometry/QuadMesh.cs b/src/cs/vim/Vim.Format.Core/Geometry/QuadMesh.cs deleted file mode 100644 index deb3af9f..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/QuadMesh.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics; -using Vim.Util; -using Vim.G3d; - -namespace Vim.Format.Geometry -{ - /// - /// This is a quadrilateral mesh. Note that it does not implement the IMesh interface, - /// but does implement IGeometryAttributes and inherits from a G3D. - /// - public class QuadMesh : G3D, IGeometryAttributes - { - public QuadMesh(IEnumerable attributes) - : base(attributes.Append(new[] { 4 }.ToObjectFaceSizeAttribute())) - => Debug.Assert(NumCornersPerFace == 4); - - public IMesh ToTriMesh() - => this.TriangulateQuadMesh().ToIMesh(); - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/SceneExtensions.cs b/src/cs/vim/Vim.Format.Core/Geometry/SceneExtensions.cs deleted file mode 100644 index 8f6e2f9a..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/SceneExtensions.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Vim.Util; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - public static class SceneExtensions - { - public static IMesh TransformedMesh(this ISceneNode node) - => node.GetMesh()?.Transform(node.Transform); - - public static IEnumerable TransformedMeshes(this IScene scene) - => scene.Nodes.Where(n => n.GetMesh() != null).Select(TransformedMesh); - - public static IMesh ToIMesh(this IScene scene) - => scene.TransformedMeshes().Merge(); - - public static bool HasLoop(this ISceneNode n) - { - if (n == null) return false; - var visited = new HashSet(); - for (; n != null; n = n.Parent) - { - if (visited.Contains(n)) - return true; - visited.Add(n); - } - - return false; - } - - public static IMesh MergedGeometry(this IScene scene) - => scene.Nodes.ToEnumerable().MergedGeometry(); - - public static IMesh MergedGeometry(this IEnumerable nodes) - => nodes.Where(n => n.GetMesh() != null).Select(TransformedMesh).Merge(); - - public static Matrix4x4 LocalTransform(this ISceneNode node) - => node.Parent != null - ? node.Transform * node.Parent.Transform.Inverse() - : node.Transform; - - public static IEnumerable AllDistinctMeshes(this IScene scene) - => scene.UntransformedMeshes().Where(x => x != null).Distinct(); - - public static IndexedSet GeometryLookup(this IScene scene) - => scene.Meshes.ToEnumerable().ToIndexedSet(); - - public static IArray UntransformedMeshes(this IScene scene) - => scene.Nodes.Select(n => n.GetMesh()); - - public static bool HasGeometry(this IScene scene) - => scene.Nodes.Any(n => n.GetMesh() != null); - - public static Dictionary GeometryCounts(this IScene scene) - => scene.Nodes.ToEnumerable().CountInstances(x => x.GetMesh()); - - public static IEnumerable AllVertices(this IScene scene) - => scene.TransformedMeshes().SelectMany(g => g.Vertices.ToEnumerable()); - - public static AABox BoundingBox(this IScene scene) - => AABox.Create(scene.AllVertices()); - - public static IArray Transforms(this IScene scene) - => scene.Nodes.Select(n => n.Transform); - - public static IArray NodePositions(this IScene scene) - => scene.Transforms().Select(m => m.Translation); - - public static AABox NodePositionBoundingBox(this IScene scene) - => AABox.Create(scene.NodePositions().ToEnumerable()); - - public static Sphere BoundingSphere(this IScene scene) - => scene.BoundingBox().ToSphere(); - - public static float BoundingRadius(this IScene scene) - => scene.BoundingSphere().Radius; - - public static IArray TransformedVertices(this ISceneNode node) - => node.TransformedMesh()?.Vertices; - - public static AABox TransformedBoundingBox(this ISceneNode node) - => AABox.Create(node.TransformedVertices()?.ToEnumerable()); - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/Serialization.cs b/src/cs/vim/Vim.Format.Core/Geometry/Serialization.cs deleted file mode 100644 index 8a7f15a0..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/Serialization.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Vim.G3d; - -namespace Vim.Format.Geometry -{ - public static class Serialization - { - public static IMesh ReadG3D(string filePath) - => G3D.Read(filePath).ToIMesh(); - - public static G3D ToG3d(this IMesh mesh) - => mesh is G3D r ? r : mesh.Attributes.ToG3d(); - - public static void WriteG3d(this IMesh mesh, string filePath) - => mesh.ToG3d().Write(filePath); - - public static void WriteObj(this IMesh mesh, string filePath) - => mesh.ToG3d().WriteObj(filePath); - - public static void WritePly(this IMesh mesh, string filePath) - => mesh.ToG3d().WritePly(filePath); - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/Topology.cs b/src/cs/vim/Vim.Format.Core/Geometry/Topology.cs deleted file mode 100644 index f7ef75c5..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/Topology.cs +++ /dev/null @@ -1,236 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Vim.G3d; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - /// - /// The base class of topology face, topology edge, topology vertex, and topology element - /// - public class TopoElement - { - public TopoElement(Topology topology, int index) - => (Topology, Index) = (topology, index); - public Topology Topology { get; } - public int Index { get; } - } - - /// - /// A topological face. - /// - public class TopoFace : TopoElement - { - public TopoFace(Topology topology, int index) - : base(topology, index) - => Debug.Assert(index >= 0 && index < topology.Mesh.NumFaces); - } - - /// - /// A directed edge around a polygon (aka a half-edge). There is exactly one half-edge per "corner" in a mesh. - /// A non-border edge in a manifold mesh has exactly two half-edges, and a border edge has one edge. - /// - public class TopoEdge : TopoElement - { - public TopoEdge(Topology topology, int index) - : base(topology, index) - => Debug.Assert(index >= 0 && index < topology.Mesh.NumCorners); - } - - /// - /// A vertex in the mesh. - /// - public class TopoVertex : TopoElement - { - public TopoVertex(Topology topology, int index) - : base(topology, index) - => Debug.Assert(index >= 0 && index < topology.Mesh.NumVertices); - } - - /// - /// Also called a "face-corner". Associated with exactly one face, and one vertex. - /// A vertex may be associated with multiple corners - /// - public class TopoCorner : TopoElement - { - public TopoCorner(Topology topology, int index) - : base(topology, index) - => Debug.Assert(index >= 0 && index < topology.Mesh.NumCorners); - } - - /// - /// This class is used to make efficient topological queries for an IGeometry. - /// Construction is a O(N) operation, so it is not created automatically. - /// - public class Topology - { - public TopoFace Face(int f) - => new TopoFace(this, f); - - public TopoEdge Edge(int e) - => new TopoEdge(this, e); - - public Topology(IMesh m) - { - Mesh = m; - Corners = Mesh.Indices.Indices(); - Faces = Mesh.NumFaces.Range(); - Vertices = Mesh.Vertices.Indices(); - - // Compute the mapping from vertex indices to faces that reference them - VerticesToFaces = new List[m.Vertices.Count]; - for (var c = 0; c < m.Indices.Count; ++c) - { - var v = m.Indices[c]; - var f = m.CornerToFace(c); - - Debug.Assert(f.Within(0, m.NumFaces)); - Debug.Assert(v.Within(0, m.NumVertices)); - Debug.Assert(c.Within(0, m.NumCorners)); - - if (VerticesToFaces[v] == null) - VerticesToFaces[v] = new List { f }; - else - VerticesToFaces[v].Add(f); - } - - // NOTE: the same edge can occur in more than two faces, only in non-manifold meshes - - // Compute the face on the other side of an edge - EdgeToOtherFace = (-1).Repeat(Mesh.NumCorners).ToArray(); - for (var c = 0; c < Mesh.NumCorners; ++c) - { - var c2 = NextCorner(c); - var f0 = CornerToFace(c); - foreach (var f1 in FacesFromCorner(c).ToEnumerable()) - { - if (f1 != f0) - { - foreach (var f2 in FacesFromCorner(c2).ToEnumerable()) - { - if (f2 == f1) - { - if (EdgeToOtherFace[c] != -1) - NonManifold = true; - EdgeToOtherFace[c] = f2; - } - } - } - } - } - - // TODO: there is some serious validation I coudl be doing doing here. - } - - public IMesh Mesh { get; } - - public List[] VerticesToFaces { get; } - public int[] EdgeToOtherFace { get; } // Assumes manifold meshes - public bool NonManifold { get; } - public IArray Corners { get; } - public IArray Vertices { get; } - public IArray Edges => Corners; - public IArray Faces { get; } - - public int CornerToFace(int i) - => Mesh.CornerToFace(i); - - public IArray FacesFromVertexIndex(int v) - => VerticesToFaces[v]?.ToIArray() ?? 0.Repeat(0); - - public IArray FacesFromCorner(int c) - => FacesFromVertexIndex(Mesh.Indices[c]); - - public int VertexIndexFromCorner(int c) - => Mesh.Indices[c]; - - /// - /// Differs from neighbour faces in that the faces have to share an edge, not just a vertex. - /// An alternative construction would have been to getNeighbourFaces and filter out those that don't share - /// - public IEnumerable BorderingFacesFromFace(int f) - => EdgesFromFace(f).Select(BorderFace).Where(bf => bf >= 0); - - public int BorderFace(int e) - => EdgeToOtherFace[e]; - - public bool IsBorderEdge(int e) - => EdgeToOtherFace[e] < 0; - - public bool IsBorderFace(int f) - => EdgesFromFace(f).Any(IsBorderEdge); - - public IArray CornersFromFace(int f) - => Mesh.NumCornersPerFace.Range().Add(FirstCornerInFace(f)); - - public IArray EdgesFromFace(int f) - => CornersFromFace(f); - - public int FirstCornerInFace(int f) - => f * Mesh.NumCornersPerFace; - - public bool FaceHasCorner(int f, int c) - => CornersFromFace(f).Contains(c); - - public int NextCorner(int c) - { - var f = CornerToFace(c); - var begin = FirstCornerInFace(f); - var end = begin + Mesh.NumCornersPerFace; - Debug.Assert(c >= begin); - Debug.Assert(c < end); - var c2 = c + 1; - if (c2 < end) - return c2; - Debug.Assert(c2 == end); - return begin; - } - - public IArray CornersFromEdge(int e) - => LinqArray.LinqArray.Create(e, NextCorner(e)); - - public IArray VertexIndicesFromEdge(int e) - => CornersFromEdge(e).Select(VertexIndexFromCorner); - - public IArray VertexIndicesFromFace(int f) - => Mesh.Indices.SelectByIndex(LinqArray.LinqArray.Create(f * 3, f * 3 + 1, f * 3 + 2)); - - public IEnumerable NeighbourVertices(int v) - => FacesFromVertexIndex(v).SelectMany(f => VertexIndicesFromFace(f)).Where(v2 => v2 != v).Distinct(); - - public IEnumerable BorderEdges - => Edges.Where(IsBorderEdge); - - public IEnumerable BorderFaces - => Faces.Where(IsBorderFace); - - public int EdgeFirstCorner(int e) - => e; - - public int EdgeNextCorner(int e) - => NextCorner(e); - - public int EdgeFirstVertex(int e) - => VertexIndexFromCorner(EdgeFirstCorner(e)); - - public int EdgeNextVertex(int e) - => VertexIndexFromCorner(EdgeFirstCorner(e)); - - public IArray EdgeVertices(int e) - => LinqArray.LinqArray.Create(EdgeFirstVertex(e), EdgeNextVertex(e)); - - public Vector3 PointFromVertex(int v) - => Mesh.Vertices[v]; - - public IArray EdgePoints(int e) - => EdgeVertices(e).Select(PointFromVertex); - } - - public static class TopologyExtensions - { - public static IMesh Mesh(this TopoElement self) - => self.Topology.Mesh; - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/TriMesh.cs b/src/cs/vim/Vim.Format.Core/Geometry/TriMesh.cs deleted file mode 100644 index 17678088..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/TriMesh.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics; -using Vim.G3d; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - /// - /// A triangular mesh data structure. - /// - public class TriMesh : G3D, IMesh - { - public TriMesh(IEnumerable attributes) - : base(attributes) - => Debug.Assert(NumCornersPerFace == 3); - - public IMesh Transform(Matrix4x4 mat) - => ((IGeometryAttributes)this).Transform(mat).ToIMesh(); - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/Validation.cs b/src/cs/vim/Vim.Format.Core/Geometry/Validation.cs deleted file mode 100644 index 5ccca143..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/Validation.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; -using Vim.BFast; -using Vim.G3d; -using Vim.LinqArray; - -namespace Vim.Format.Geometry -{ - public static class Validation - { - public static void ValidateTableRows(this Document doc) - { - foreach (var et in doc.EntityTables.Values.ToArray()) - { - foreach (var c in et.IndexColumns.Values.ToArray()) - { - if (c.Array.Length != et.NumRows) - throw new Exception($"Expected array length {c.Array.Length} of column {c.Name} to be the same as number of rows {et.NumRows}"); - } - - foreach (var c in et.DataColumns.Values.ToArray()) - { - if (c.NumElements() != et.NumRows) - throw new Exception($"Expected array length {c.NumElements()} of column {c.Name} to be the same as number of rows {et.NumRows}"); - } - - foreach (var c in et.StringColumns.Values.ToArray()) - { - if (c.Array.Length != et.NumRows) - throw new Exception($"Expected array length {c.Array.Length} of column {c.Name} to be the same as number of rows {et.NumRows}"); - } - } - } - - public static void ValidateIndexColumns(this Document doc) - { - foreach (var et in doc.EntityTables.Values.ToArray()) - { - foreach (var ic in et.IndexColumns.Values.ToEnumerable()) - { - var table = ic.GetRelatedTable(doc); - if (table == null) - throw new Exception($"Could not find related table for index column {ic.Name}"); - } - } - } - - public static string[] RequiredAttributeNames => new [] - { - // Vertices - CommonAttributes.Position, - CommonAttributes.Index, - - // Meshes - CommonAttributes.MeshSubmeshOffset, - - // Submeshes - CommonAttributes.SubmeshIndexOffset, - - // Instances - CommonAttributes.InstanceMesh, - CommonAttributes.InstanceTransform, - }; - - public static void ValidateGeometryAttributes(this Document doc) - { - var attributes = doc.Geometry.Attributes; - var attributeNameSet = new HashSet(attributes.Select(a => a.Name).ToEnumerable()); - foreach (var attributeName in RequiredAttributeNames) - { - if (!attributeNameSet.Contains(attributeName)) - throw new Exception($"Required attribute {attributeName} was not found."); - } - } - - public static void ValidateAssets(this Document doc) - { - foreach (var asset in doc.Assets.Values.ToEnumerable()) - AssetInfo.Parse(asset.Name); // This will throw if it fails to parse. - } - - public static void Validate(this Document doc) - { - doc.ValidateTableRows(); - doc.ValidateIndexColumns(); - doc.ValidateGeometryAttributes(); - doc.ValidateAssets(); - } - - // TODO: ValidateShapes() to validate VIM files which contain optional 2d data (shapes/overlays). - - public static void ValidateIndices(this IMesh mesh) - { - foreach (var index in mesh.Indices.ToEnumerable()) - { - if (index < 0 || index >= mesh.NumVertices) - throw new Exception($"Invalid mesh index: {index}. Expected a value greater or equal to 0 and less than {mesh.NumVertices}"); - } - } - - public static void Validate(this IMesh mesh) - { - mesh.ValidateIndices(); - } - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/VimMaterial.cs b/src/cs/vim/Vim.Format.Core/Geometry/VimMaterial.cs deleted file mode 100644 index 4799219f..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/VimMaterial.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Vim.G3d; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - public interface IMaterial - { - Vector4 Color { get; } - float Smoothness { get; } - float Glossiness { get; } - } - - public class VimMaterial : IMaterial - { - public G3dMaterial Material; - public VimMaterial(G3dMaterial material) => Material = material; - public Vector4 Color => Material.Color; - public float Smoothness => Material.Smoothness; - public float Glossiness => Material.Glossiness; - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/VimMaterialNext.cs b/src/cs/vim/Vim.Format.Core/Geometry/VimMaterialNext.cs new file mode 100644 index 00000000..cf179bf6 --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/Geometry/VimMaterialNext.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using Vim.G3d; +using Vim.Math3d; + +namespace Vim.Format.Geometry +{ + public interface IMaterial + { + Vector4 Color { get; } + float Smoothness { get; } + float Glossiness { get; } + } + + + public class VimMaterialNext : IMaterial + { + public G3dVim g3d; + public int index; + + public static IEnumerable FromG3d(G3dVim g3d) + { + for(var i =0; i < g3d.GetMaterialCount(); i++) + { + yield return new VimMaterialNext(g3d, i); + } + } + + public Vector4 Color => g3d.MaterialColors[index]; + public float Smoothness => g3d.MaterialSmoothness[index]; + public float Glossiness => g3d.MaterialGlossiness[index]; + public VimMaterialNext(G3dVim g3d, int index) + { + this.g3d = g3d; + this.index = index; + } + } +} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/VimMesh.cs b/src/cs/vim/Vim.Format.Core/Geometry/VimMesh.cs new file mode 100644 index 00000000..08a29fad --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/Geometry/VimMesh.cs @@ -0,0 +1,433 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Vim.G3d; +using Vim.Math3d; + +namespace Vim.Format.Geometry +{ + public class VimMesh : ITransformable3D + { + public int SubmeshCount => submeshIndexCounts.Length; + + public int NumCornersPerFace => 3; + + public int NumVertices => vertices.Length; + + public int NumCorners => indices.Length; + + public int NumFaces => indices.Length / 3; + + + public int[] indices; + public Vector3[] vertices; + public int[] submeshIndexOffsets; + public int[] submeshMaterials; + public int[] submeshIndexCounts; + + public int GetIndexCount() => indices.Length; + public int GetVertexCount() => vertices.Length; + + + public VimMesh() + { + + } + + public VimMesh(Vector3[] vertices) + : this(Enumerable.Range(0, vertices.Length).ToArray(), vertices) + { + + } + + public VimMesh( + int[] indices, + Vector3[] vertices, + int[] submeshIndexOffsets = null, + int[] submeshMaterials = null, + int[] submeshIndexCounts = null + ) + { + this.indices = indices; + this.vertices = vertices; + + this.submeshIndexOffsets = submeshIndexOffsets ?? new int[1] { 0 }; + this.submeshMaterials = submeshMaterials ?? new int[1] { -1 }; + this.submeshIndexCounts = submeshIndexCounts ?? ComputeCounts(this.submeshIndexOffsets, this.indices.Length); + } + + public G3dVim ToG3d() + { + return new G3dVim( + indices: indices, + positions: vertices, + instanceMeshes: new[] { 0 }, + instanceTransforms: new[] { Matrix4x4.Identity }, + instanceParents: null, + instanceFlags: null, + meshSubmeshOffsets: new[] { 0 }, + submeshIndexOffsets: new[] { 0 }, + submeshMaterials: new[] { -1 }, + materialColors: null, + materialGlossiness: null, + materialSmoothness: null, + shapeVertices: null, + shapeColors: null, + shapeVertexOffsets: null, + shapeWidths: null); + } + + private static int[] ComputeCounts(int[] offsets, int max) + { + var result = new int[offsets.Length]; + for (var i = 0; i < result.Length - 1; i++) + { + result[i] = offsets[i + 1] - offsets[i]; + } + var last = result.Length - 1; + result[last] = max - offsets[last]; + return result; + } + + + public VimMesh(int indexCount, int vertexCount, int submeshCount) + { + indices = new int[indexCount]; + vertices = new Vector3[vertexCount]; + submeshIndexOffsets = new int[submeshCount]; + submeshMaterials = new int[submeshCount]; + submeshIndexCounts = new int[submeshCount]; + } + + public VimMesh Clone() + { + var mesh = new VimMesh( + indices, + vertices, + submeshIndexOffsets, + submeshMaterials + ); + return mesh; + } + public VimMesh Transform(Matrix4x4 mat) + { + var mesh = Clone(); + + for (var i = 0; i < vertices.Length; i++) + { + mesh.vertices[i] = vertices[i].Transform(mat); + } + + return mesh; + } + + public static VimMesh FromG3d(G3dVim g3d, int index) + { + var mesh = new VimMesh(); + + var vStart = g3d.GetMeshVertexStart(index); + var vEnd = g3d.GetMeshVertexEnd(index); + mesh.vertices = new Vector3[vEnd - vStart]; + for (var i = 0; i < mesh.vertices.Length; i++) + { + var v = vStart + i; + mesh.vertices[i] = g3d.Positions[v]; + } + + var iStart = g3d.GetMeshIndexStart(index); + var iEnd = g3d.GetMeshIndexEnd(index); + mesh.indices = new int[iEnd - iStart]; + for (var i = 0; i < mesh.indices.Length; i++) + { + var j = iStart + i; + mesh.indices[i] = g3d.Indices[j] - vStart; + } + + var sStart = g3d.GetMeshSubmeshStart(index); + var sEnd = g3d.GetMeshSubmeshEnd(index); + mesh.submeshIndexOffsets = new int[sEnd - sStart]; + mesh.submeshMaterials = new int[sEnd - sStart]; + mesh.submeshIndexCounts = new int[sEnd - sStart]; + + for (var i = 0; i < mesh.submeshMaterials.Length; i++) + { + var s = sStart + i; + mesh.submeshIndexOffsets[i] = g3d.SubmeshIndexOffsets[s] - iStart; + mesh.submeshMaterials[i] = g3d.SubmeshMaterials[s]; + mesh.submeshIndexCounts[i] = g3d.GetSubmeshIndexCount(s); + } + + return mesh; + } + + public static VimMesh FromG3d(G3dVim g3d) + { + var mesh = new VimMesh( + g3d.Indices, + g3d.Positions, + g3d.SubmeshIndexOffsets, + g3d.SubmeshMaterials + ); + + return mesh; + } + + public static VimMesh FromQuad(int[] indices, Vector3[] vertices) + { + if (indices.Length % 4 != 0) + { + throw new ArgumentException("Indices count should be a multiple of 4."); + } + var faceCount = indices.Length / 4; + var triIndices = new int[faceCount * 6]; + var cur = 0; + for (var i = 0; i < faceCount; ++i) + { + triIndices[cur++] = indices[i * 4 + 0]; + triIndices[cur++] = indices[i * 4 + 1]; + triIndices[cur++] = indices[i * 4 + 2]; + triIndices[cur++] = indices[i * 4 + 0]; + triIndices[cur++] = indices[i * 4 + 2]; + triIndices[cur++] = indices[i * 4 + 3]; + } + return new VimMesh(triIndices, vertices); + } + + public static IEnumerable GetAllMeshes(G3dVim g3d) + { + return Enumerable.Range(0, g3d.GetMeshCount()).Select(i => FromG3d(g3d, i)); + } + + public void SetVertices(Vector3[] vertices) + { + this.vertices = vertices; + } + public void SetIndices(int[] indices) + { + this.indices = indices; + } + + public void Validate() + { + //TODO: Validate better + ValidateIndices(); + } + private void ValidateIndices() + { + foreach (var index in indices) + { + if (index < 0 || index >= NumVertices) + throw new Exception($"Invalid mesh index: {index}. Expected a value greater or equal to 0 and less than {NumVertices}"); + } + } + } + + public static class MeshCommonExtensions + { + public static VimMesh ReverseWindingOrder(this VimMesh mesh) + { + var result = mesh.Clone(); + var count = mesh.indices.Length; + var indices = new int[count]; + for (var i = 0; i < count; i += 3) + { + indices[i + 0] = mesh.indices[i + 2]; + indices[i + 1] = mesh.indices[i + 1]; + indices[i + 2] = mesh.indices[i + 0]; + } + result.SetIndices(indices); + return result; + } + + + public static int[] GetFaceMaterials(this VimMesh mesh) + { + // SubmeshIndexOffsets: [0, A, B] + // SubmeshIndexCount: [X, Y, Z] + // SubmeshMaterials: [L, M, N] + // --- + // FaceMaterials: [...Repeat(L, X / 3), ...Repeat(M, Y / 3), ...Repeat(N, Z / 3)] <-- divide by 3 for the number of corners per Triangular face + var numCornersPerFace = mesh.NumCornersPerFace; + return mesh.submeshIndexCounts + .SelectMany((indexCount, i) => Enumerable.Repeat(mesh.submeshMaterials[i], indexCount / numCornersPerFace)) + .ToArray(); + } + + public static VimMesh Merge2(this VimMesh mesh, params VimMesh[] others) + { + var meshes = Enumerable.Empty() + .Append(mesh) + .Concat(others) + .ToArray(); + + return meshes.Merge(); + } + + public static VimMesh Merge(this VimMesh[] meshes) + { + void Merge(int[] from, int[] to, int offset, int increment) + { + for (var i = 0; i < from.Length; i++) + { + to[i + offset] = from[i] + increment; + } + } + + // Init arrays + var indexCount = meshes.Sum(m => m.indices.Length); + var vertexCount = meshes.Sum(m => m.vertices.Length); + var submeshCount = meshes.Sum(m => m.submeshIndexOffsets.Length); + var result = new VimMesh(indexCount, vertexCount, submeshCount); + + var indexOffset = 0; + var vertexOffset = 0; + var submeshOffset = 0; + // Copy and merge meshes + for (var m = 0; m < meshes.Length; m++) + { + var mesh = meshes[m]; + + Merge(mesh.indices, result.indices, indexOffset, vertexOffset); + mesh.vertices.CopyTo(result.vertices, vertexOffset); + mesh.submeshMaterials.CopyTo(result.submeshMaterials, submeshOffset); + mesh.submeshIndexCounts.CopyTo(result.submeshIndexCounts, submeshOffset); + Merge(mesh.submeshIndexOffsets, result.submeshIndexOffsets, submeshOffset, indexOffset); + + indexOffset += mesh.indices.Length; + vertexOffset += mesh.vertices.Length; + submeshOffset += mesh.submeshIndexOffsets.Length; + + } + return result; + } + + private static (int, List)[] GroupSubmeshesByMaterials(this VimMesh mesh) + { + var submeshCount = mesh.submeshIndexOffsets.Length; + var map = new Dictionary>(); + for (var i = 0; i < submeshCount; i++) + { + var mat = mesh.submeshMaterials[i]; + if (map.ContainsKey(mat)) + { + map[mat].Add(i); + } + else + { + map.Add(mat, new List() { i }); + } + } + return map.Select(kvp => (kvp.Key, kvp.Value)).ToArray(); + } + + private static Triangle VertexIndicesToTriangle(this VimMesh mesh, Int3 indices) + => new Triangle(mesh.vertices[indices.X], mesh.vertices[indices.Y], mesh.vertices[indices.Z]); + + public static bool Planar(this VimMesh mesh, float tolerance = Math3d.Constants.Tolerance) + { + if (mesh.NumFaces <= 1) return true; + var normal = mesh.Triangle(0).Normal; + return mesh.ComputedNormals().All(n => n.AlmostEquals(normal, tolerance)); + } + + private static Vector3[] ComputedNormals(this VimMesh mesh) + => mesh.Triangles().Select(t => t.Normal).ToArray(); + + public static Triangle Triangle(this VimMesh mesh, int face) + => mesh.VertexIndicesToTriangle(mesh.FaceVertexIndices(face)); + + public static Triangle[] Triangles(this VimMesh mesh) + => Enumerable.Range(0, mesh.NumFaces).Select(mesh.Triangle).ToArray(); + + public static Vector3 Center(this VimMesh mesh) + => mesh.BoundingBox().Center; + + public static AABox BoundingBox(this VimMesh mesh) + => AABox.Create(mesh.vertices); + + public static Int3 FaceVertexIndices(this VimMesh mesh, int faceIndex) + => new Int3(mesh.indices[faceIndex * 3], mesh.indices[faceIndex * 3 + 1], mesh.indices[faceIndex * 3 + 2]); + + public static bool GeometryEquals(this VimMesh mesh, VimMesh other, float tolerance = Math3d.Constants.Tolerance) + { + if (!mesh.indices.SequenceEqual(other.indices)) + return false; + + if (!mesh.submeshIndexOffsets.SequenceEqual(other.submeshIndexOffsets)) + return false; + + if (!mesh.submeshMaterials.SequenceEqual(other.submeshMaterials)) + return false; + + if (!mesh.submeshIndexCounts.SequenceEqual(other.submeshIndexCounts)) + return false; + + if (mesh.vertices.Length != other.vertices.Length) + return false; + + for (var i = 0; i < mesh.vertices.Length; i++) + { + if (!mesh.vertices[i].AlmostEquals(other.vertices[i], tolerance)) + return false; + } + + return true; + } + + public static (int mat, VimMesh mesh)[] SplitByMaterial(this VimMesh mesh) + { + var map = mesh.GroupSubmeshesByMaterials(); + + var result = new (int, VimMesh)[map.Length]; + if (map.Length == 1) + { + result[0] = (mesh.submeshMaterials[0], mesh); + return result; + } + + for (var i = 0; i < map.Length; i++) + { + var (mat, subs) = map[i]; + var pick = mesh.PickSubmeshes(subs); + result[i] = (mat, pick); + } + return result; + } + + private static VimMesh PickSubmeshes(this VimMesh mesh, IList submeshes) + { + var map = mesh.GroupSubmeshesByMaterials(); + + // Allocate arrays of the final sizes + var indexCount = submeshes.Sum(s => mesh.submeshIndexCounts[s]); + var result = new VimMesh(indexCount, indexCount, submeshes.Count); + + var indexOffset = 0; + var index = 0; + for (var s = 0; s < submeshes.Count; s++) + { + var submesh = submeshes[s]; + + // copy indices at their new positions + var indexStart = mesh.submeshIndexOffsets[submesh]; + var indexEnd = indexStart + mesh.submeshIndexCounts[submesh]; + for (var i = indexStart; i < indexEnd; i++) + { + result.indices[index] = indexOffset + i - indexStart; + result.vertices[index] = mesh.vertices[mesh.indices[i]]; + index++; + } + + // submesh data is mostly the same + result.submeshIndexCounts[s] = mesh.submeshIndexCounts[submesh]; + result.submeshMaterials[s] = mesh.submeshMaterials[submesh]; + result.submeshIndexOffsets[s] = indexOffset; + + // Update offset for next submesh + indexOffset += indexEnd - indexStart; + } + + return result; + } + } +} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/VimShapeNext.cs b/src/cs/vim/Vim.Format.Core/Geometry/VimShapeNext.cs new file mode 100644 index 00000000..7c63bdd1 --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/Geometry/VimShapeNext.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using Vim.G3d; +using Vim.Math3d; + +namespace Vim.Format.Geometry +{ + public class VimShapeNext + { + public readonly G3dVim g3d; + public readonly int Index; + public readonly ArraySegment Vertices; + + public Vector4 Color => g3d.ShapeColors[Index]; + public float Width => g3d.ShapeWidths[Index]; + + public static IEnumerable FromG3d(G3dVim g3d) + { + for(var i =0; i < g3d.GetShapeCount(); i++) + { + yield return new VimShapeNext(g3d, i); + } + } + + public VimShapeNext(G3dVim g3d, int index) + { + var start = g3d.GetShapeVertexStart(index); + var count = g3d.GetShapeVertexCount(index); + + Vertices = new ArraySegment(g3d.ShapeVertices, start, count); + } + } +} diff --git a/src/cs/vim/Vim.Format.Core/SerializableDocument.cs b/src/cs/vim/Vim.Format.Core/SerializableDocument.cs index 6b834739..6acaa935 100644 --- a/src/cs/vim/Vim.Format.Core/SerializableDocument.cs +++ b/src/cs/vim/Vim.Format.Core/SerializableDocument.cs @@ -1,44 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; -using Vim.BFast; +using System.Text; +using Vim.BFastLib; +using Vim.BFastLib.Core; +using Vim.G3d; namespace Vim.Format { - /// - /// Tracks all of the data for a particular entity type in a conceptual table. - /// A column maybe a relation to another entity table (IndexColumn) - /// a data value stored as a double (DataColumn) or else - /// it is string data, stored as an index into the global lookup table (StringColumn). - /// - public class SerializableEntityTable - { - /// - /// Name of - /// - public string Name; - - /// - /// Relation to another entity table. For example surface to element. - /// - public List> IndexColumns = new List>(); - - /// - /// Data encoded as strings in the global string table - /// - public List> StringColumns = new List>(); - - /// - /// Numeric data encoded as byte, int, float, or doubles - /// - public List DataColumns = new List(); - - public IEnumerable ColumnNames - => IndexColumns.Select(c => c.Name) - .Concat(StringColumns.Select(c => c.Name)) - .Concat(DataColumns.Select(c => c.Name)); - } - /// /// Controls what parts of the VIM file are loaded /// @@ -82,11 +51,99 @@ public class SerializableDocument /// /// The uninstanced / untransformed geometry /// - public G3d.G3D Geometry; + public G3dVim GeometryNext; /// /// The originating file name (if provided) /// public string FileName; + public BFast ToBFast() + { + var bfast = new BFast(); + if(Header != null) + { + bfast.SetArray(BufferNames.Header, Header.ToBytes()); + } + + if(Assets != null) + { + var assets = new BFast(); + foreach (var asset in Assets) + { + assets.SetArray(asset.Name, asset.ToArray()); + } + bfast.SetBFast(BufferNames.Assets, assets); + } + + if(EntityTables != null) + { + var entities = new BFast(); + foreach (var entity in EntityTables) + { + entities.SetBFast(entity.Name, entity.ToBFast()); + } + bfast.SetBFast(BufferNames.Entities, entities); + } + + if (StringTable != null) + { + bfast.SetArray(BufferNames.Strings, BFastStrings.Pack(StringTable)); + } + + if(GeometryNext != null) + { + bfast.SetBFast(BufferNames.Geometry, GeometryNext.ToBFast()); + } + + return bfast; + } + + public static SerializableDocument FromPath(string path, LoadOptions options = null) + { + var doc = BFastHelpers.Read(path, b => FromBFast(b)); + doc.FileName = path; + return doc; + } + + public static SerializableDocument FromBFast(BFast bfast, LoadOptions options = null) + { + var doc = new SerializableDocument(); + doc.Options = options ?? new LoadOptions(); + doc.Header = SerializableHeader.FromBytes(bfast.GetArray(BufferNames.Header)); + if (!doc.Options.SkipAssets) + { + var asset = bfast.GetBFast(BufferNames.Assets); + doc.Assets = asset.ToNamedBuffers().ToArray(); + } + var strs = bfast.GetArray(BufferNames.Strings); + doc.StringTable = Encoding.UTF8.GetString(strs).Split('\0'); + + if (!doc.Options.SkipGeometry) + { + var geo = bfast.GetBFast(BufferNames.Geometry); + doc.GeometryNext = new G3dVim(geo); + } + + var entities = bfast.GetBFast(BufferNames.Entities); + doc.EntityTables = GetEntityTables(entities, doc.Options.SchemaOnly).ToList(); + return doc; + } + + /// + /// Enumerates the SerializableEntityTables contained in the given entities buffer. + /// + private static IEnumerable GetEntityTables( + BFast bfast, + bool schemaOnly) + { + + foreach (var entry in bfast.Entries) + { + var b = bfast.GetBFast(entry); + var table = SerializableEntityTable.FromBFast(b, schemaOnly); + table.Name = entry; + yield return table; + } + } } } diff --git a/src/cs/vim/Vim.Format.Core/SerializableEntityTable.cs b/src/cs/vim/Vim.Format.Core/SerializableEntityTable.cs new file mode 100644 index 00000000..5eb49c01 --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/SerializableEntityTable.cs @@ -0,0 +1,124 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Vim.BFastLib; + +namespace Vim.Format +{ + /// + /// Tracks all of the data for a particular entity type in a conceptual table. + /// A column maybe a relation to another entity table (IndexColumn) + /// a data value stored as a double (DataColumn) or else + /// it is string data, stored as an index into the global lookup table (StringColumn). + /// + public class SerializableEntityTable + { + /// + /// Name of + /// + public string Name; + + /// + /// Relation to another entity table. For example surface to element. + /// + public List> IndexColumns = new List>(); + + /// + /// Data encoded as strings in the global string table + /// + public List> StringColumns = new List>(); + + /// + /// Numeric data encoded as byte, int, float, or doubles + /// + public List DataColumns = new List(); + + public IEnumerable AllColumns + => IndexColumns + .Concat(StringColumns) + .Concat(DataColumns); + + private static readonly Regex TypePrefixRegex = new Regex(@"(\w+:).*"); + + public static string GetTypeFromName(string name) + { + var match = TypePrefixRegex.Match(name); + return match.Success ? match.Groups[1].Value : ""; + } + + /// + /// Returns a SerializableEntityTable based on the given buffer reader. + /// + public static SerializableEntityTable FromBFast( + BFast bfast, + bool schemaOnly + ) + { + var et = new SerializableEntityTable(); + foreach (var entry in bfast.Entries) + { + var typePrefix = GetTypeFromName(entry); + + switch (typePrefix) + { + case VimConstants.IndexColumnNameTypePrefix: + { + //TODO: replace named buffer with arrays + var col = schemaOnly ? new int[0] : bfast.GetArray(entry); + et.IndexColumns.Add(col.ToNamedBuffer(entry)); + break; + } + case VimConstants.StringColumnNameTypePrefix: + { + var col = schemaOnly ? new int[0] : bfast.GetArray(entry); + et.StringColumns.Add(col.ToNamedBuffer(entry)); + break; + } + case VimConstants.IntColumnNameTypePrefix: + { + var col = schemaOnly ? new int[0] : bfast.GetArray(entry); + et.DataColumns.Add(col.ToNamedBuffer(entry)); + break; + } + case VimConstants.LongColumnNameTypePrefix: + { + var col = schemaOnly ? new long[0] : bfast.GetArray(entry); + et.DataColumns.Add(col.ToNamedBuffer(entry)); + break; + } + case VimConstants.DoubleColumnNameTypePrefix: + { + var col = schemaOnly ? new double[0] : bfast.GetArray(entry); + et.DataColumns.Add(col.ToNamedBuffer(entry)); + break; + } + case VimConstants.FloatColumnNameTypePrefix: + { + var col = schemaOnly ? new float[0] : bfast.GetArray(entry); + et.DataColumns.Add(col.ToNamedBuffer(entry)); + break; + } + case VimConstants.ByteColumnNameTypePrefix: + { + var col = schemaOnly ? new byte[0] : bfast.GetArray(entry); + et.DataColumns.Add(col.ToNamedBuffer(entry)); + break; + } + // For flexibility, we ignore the columns which do not contain a recognized prefix. + } + } + + return et; + } + + public BFast ToBFast() + { + var bfast = new BFast(); + foreach (var col in AllColumns) + { + bfast.SetArray(col.Name, col.ToBytes()); + } + return bfast; + } + } +} diff --git a/src/cs/vim/Vim.Format.Core/SerializableHeader.cs b/src/cs/vim/Vim.Format.Core/SerializableHeader.cs index c1c3a97c..c8e2561f 100644 --- a/src/cs/vim/Vim.Format.Core/SerializableHeader.cs +++ b/src/cs/vim/Vim.Format.Core/SerializableHeader.cs @@ -1,30 +1,33 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; +using System.Text; +using Vim.BFastLib; using Vim.Util; namespace Vim.Format { public class SerializableHeader { - public static readonly SerializableVersion CurrentVimFormatVersion = VimFormatVersion.Current; - - public const string FormatVersionField = "vim"; - public const string IdField = "id"; - public const string RevisionField = "revision"; - public const string GeneratorField = "generator"; - public const string CreationDateField = "created"; - public const string SchemaField = "schema"; + private static readonly SerializableVersion CurrentVimFormatVersion = VimFormatVersion.Current; + + private const string FormatVersionField = "vim"; + private const string IdField = "id"; + private const string RevisionField = "revision"; + private const string GeneratorField = "generator"; + private const string CreationDateField = "created"; + private const string SchemaField = "schema"; public const string BuildField = "build"; // optional - public const char Separator = '='; - public const char EndOfLineChar = '\n'; - public const string EndOfLineString = "\n"; - public const string PersistingIdSeparator = "::"; - public const string DummyPersistingIdPrefix = "unknown_"; + private const char Separator = '='; + private const char EndOfLineChar = '\n'; + private const string EndOfLineString = "\n"; + private const string PersistingIdSeparator = "::"; + private const string DummyPersistingIdPrefix = "unknown_"; - public static readonly string[] RequiredFields = + private static readonly string[] RequiredFields = { FormatVersionField, IdField, @@ -93,6 +96,30 @@ public SerializableHeader(string generator, SerializableVersion schema, string v AddOptionalValues(values ?? new Dictionary(), versionString)) { } + public static SerializableHeader FromBytes(byte[] input) + { + return FromString(Encoding.UTF8.GetString(input)); + } + + + /// + /// Returns the VIM header from a vim file stream. + /// Will throw if the file is not a valid VIM. + /// + public static SerializableHeader FromStream(Stream stream) + { + var bfast = new BFast(stream); + var bytes = bfast.GetArray(BufferNames.Header); + if (bytes == null) return null; + return SerializableHeader.FromBytes(bytes); + } + + public static SerializableHeader FromPath(string path) + { + using (var file = new FileStream(path, FileMode.OpenOrCreate)) + return FromStream(file); + } + /// /// Parses the input. Throws exceptions if the input does not define a correctly formatted header. /// @@ -100,7 +127,7 @@ public SerializableHeader(string generator, SerializableVersion schema, string v /// /// /// - public static SerializableHeader Parse(string input) + public static SerializableHeader FromString(string input) { var lines = input.Split(EndOfLineChar) .Where(str => !string.IsNullOrEmpty(str)); @@ -119,7 +146,7 @@ public static SerializableHeader Parse(string input) { var tokens = line.Split(Separator); var numTokens = tokens.Length; - + // skip empty lines. if (numTokens == 0) continue; @@ -252,7 +279,7 @@ public override bool Equals(object obj) public override int GetHashCode() => ToString().GetHashCode(); - public static string CreatePersistingId(Guid id, Guid revision) + private static string CreatePersistingId(Guid id, Guid revision) => string.Join(PersistingIdSeparator, id.ToString(), revision.ToString()); /// @@ -273,5 +300,57 @@ public static string CreateDummyPersistingId() /// public string PersistingId => CreatePersistingId(Id, Revision); + + public byte[] ToBytes() + { + return ToString().ToBytesUtf8(); + } + } + + public static class SerializableHeaderExtensions + { + /// + /// Returns true if the SerializableHeader in the stream is successfully parsed. + /// + public static bool TryParseSerializableHeader(this Stream stream, out SerializableHeader header) + { + try + { + header = SerializableHeader.FromStream(stream); + } + catch + { + header = null; + } + return header != null; + } + + /// + /// Returns true if the SerializableHeader in the stream is successfully parsed. + /// + public static bool TryParseSerializableHeader(this FileInfo file, out SerializableHeader header) + { + using (var stream = file.OpenRead()) + { + try + { + header = SerializableHeader.FromStream(stream); + } + catch + { + header = null; + } + } + + return header != null; + } + + /// + /// Returns the VIM file's header schema version. Returns null if the header schema is not found. + /// + public static string GetSchemaVersion(this FileInfo fileInfo) + => fileInfo.TryParseSerializableHeader(out var header) + ? header.Schema?.ToString() + : null; } } diff --git a/src/cs/vim/Vim.Format.Core/Serializer.cs b/src/cs/vim/Vim.Format.Core/Serializer.cs deleted file mode 100644 index 06dce92e..00000000 --- a/src/cs/vim/Vim.Format.Core/Serializer.cs +++ /dev/null @@ -1,328 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System; -using Vim.BFast; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; -using Vim.G3d; -using Vim.Util; - -namespace Vim.Format -{ - public static class Serializer - { - public static List ToBuffers(this SerializableEntityTable table) - { - var r = new List(); - - r.AddRange(table.DataColumns); - r.AddRange(table.IndexColumns); - r.AddRange(table.StringColumns); - - return r; - } - - public static readonly Regex TypePrefixRegex = new Regex(@"(\w+:).*"); - - public static string GetTypePrefix(this string name) - { - var match = TypePrefixRegex.Match(name); - return match.Success ? match.Groups[1].Value : ""; - } - - /// - /// Returns the named buffer prefix, or null if no prefix was found. - /// - public static string GetTypePrefix(this INamedBuffer namedBuffer) - => namedBuffer.Name.GetTypePrefix(); - - /// - /// Returns a NamedBuffer representing to an entity table column. - /// If schemaOnly is enabled, the column is returned without any of its contained data; - /// this is useful for rapidly querying the schema of the entity table. - /// - public static NamedBuffer ReadEntityTableColumn( - this BFastBufferReader columnBufferReader, - bool schemaOnly) where T : unmanaged - { - var (name, size) = columnBufferReader; - - if (schemaOnly) - return new Buffer(Array.Empty()).ToNamedBuffer(name); - - return columnBufferReader - .Seek() - .ReadBufferFromNumberOfBytes(size) - .ToNamedBuffer(name); - } - - /// - /// Returns a SerializableEntityTable based on the given buffer reader. - /// - public static SerializableEntityTable ReadEntityTable( - this BFastBufferReader entityTableBufferReader, - bool schemaOnly) - { - var et = new SerializableEntityTable { Name = entityTableBufferReader.Name }; - - foreach (var colBr in entityTableBufferReader.Seek().GetBFastBufferReaders()) - { - var name = colBr.Name; - var typePrefix = name.GetTypePrefix(); - - switch (typePrefix) - { - case VimConstants.IndexColumnNameTypePrefix: - { - et.IndexColumns.Add(colBr.ReadEntityTableColumn(schemaOnly)); - break; - } - case VimConstants.StringColumnNameTypePrefix: - { - et.StringColumns.Add(colBr.ReadEntityTableColumn(schemaOnly)); - break; - } - case VimConstants.IntColumnNameTypePrefix: - { - et.DataColumns.Add(colBr.ReadEntityTableColumn(schemaOnly)); - break; - } - case VimConstants.LongColumnNameTypePrefix: - { - et.DataColumns.Add(colBr.ReadEntityTableColumn(schemaOnly)); - break; - } - case VimConstants.DoubleColumnNameTypePrefix: - { - et.DataColumns.Add(colBr.ReadEntityTableColumn(schemaOnly)); - break; - } - case VimConstants.FloatColumnNameTypePrefix: - { - et.DataColumns.Add(colBr.ReadEntityTableColumn(schemaOnly)); - break; - } - case VimConstants.ByteColumnNameTypePrefix: - { - et.DataColumns.Add(colBr.ReadEntityTableColumn(schemaOnly)); - break; - } - // For flexibility, we ignore the columns which do not contain a recognized prefix. - } - } - - return et; - } - - /// - /// Enumerates the SerializableEntityTables contained in the given entities buffer. - /// - public static IEnumerable EnumerateEntityTables( - this BFastBufferReader entitiesBufferReader, - bool schemaOnly, - Func entityTableNameFilterFunc = null) - { - var entityTableBufferReaders = entitiesBufferReader.Seek() - .GetBFastBufferReaders(br => entityTableNameFilterFunc?.Invoke(br.Name) ?? true); - - foreach (var entityTableBufferReader in entityTableBufferReaders) - { - yield return entityTableBufferReader.ReadEntityTable(schemaOnly); - } - } - - /// - /// Enumerates the SerializableEntityTables contained in the given VIM file. - /// - public static IEnumerable EnumerateEntityTables( - this FileInfo vimFileInfo, - bool schemaOnly, - Func entityTableNameFilterFunc = null) - { - using (var stream = vimFileInfo.OpenRead()) - { - var entitiesBufferReader = stream.GetBFastBufferReader(BufferNames.Entities); - if (entitiesBufferReader == null) - yield break; - - foreach (var entityTable in entitiesBufferReader.EnumerateEntityTables(schemaOnly, entityTableNameFilterFunc)) - { - yield return entityTable; - } - } - } - - public static BFastBuilder ToBFastBuilder(this IEnumerable entityTables) - { - var bldr = new BFastBuilder(); - foreach (var et in entityTables) - { - bldr.Add(et.Name, et.ToBuffers()); - } - return bldr; - } - - public static BFastBuilder ToBFastBuilder(this SerializableDocument doc) - => CreateBFastBuilder( - doc.Header, - doc.Assets, - doc.StringTable, - doc.EntityTables, - doc.Geometry.ToG3DWriter()); - - public static BFastBuilder CreateBFastBuilder( - SerializableHeader header, - IEnumerable assets, - IEnumerable stringTable, - IEnumerable entityTables, - IBFastComponent geometry) - { - var bfastBuilder = new BFastBuilder(); - bfastBuilder.Add(BufferNames.Header, header.ToString().ToBytesUtf8().ToBuffer()); - bfastBuilder.Add(BufferNames.Assets, assets ?? Array.Empty()); - bfastBuilder.Add(BufferNames.Entities, entityTables.ToBFastBuilder()); - bfastBuilder.Add(BufferNames.Strings, stringTable.PackStrings().ToBuffer()); - bfastBuilder.Add(BufferNames.Geometry, geometry); - return bfastBuilder; - } - - public static void Serialize( - Stream stream, - SerializableHeader header, - IEnumerable assets, - IEnumerable stringTable, - IEnumerable entityTables, - IBFastComponent geometry) - { - CreateBFastBuilder(header, assets, stringTable, entityTables, geometry).Write(stream); - } - - public static void Serialize(this SerializableDocument doc, Stream stream) - => doc.ToBFastBuilder().Write(stream); - - public static void Serialize(this SerializableDocument document, string filePath) - { - using (var stream = File.OpenWrite(filePath)) - document.Serialize(stream); - } - - public static SerializableHeader ToSerializableHeader(this byte[] bytes) - => SerializableHeader.Parse(Encoding.UTF8.GetString(bytes)); - - /// - /// Returns true if the SerializableHeader in the stream is successfully parsed. - /// - public static bool TryParseSerializableHeader(this Stream stream, out SerializableHeader header) - { - using (new SeekContext(stream)) - { - try - { - header = stream.ReadBFastBuffer(BufferNames.Header)?.Array.ToSerializableHeader(); - } - catch - { - header = null; - } - return header != null; - } - } - - /// - /// Returns true if the SerializableHeader in the given VIM file is successfully parsed. - /// - public static bool TryParseSerializableHeader(this FileInfo fileInfo, out SerializableHeader header) - { - using (var fs = fileInfo.OpenRead()) - { - return fs.TryParseSerializableHeader(out header); - } - } - - /// - /// Returns the VIM file's header schema version. Returns null if the header schema is not found. - /// - public static string GetSchemaVersion(this FileInfo fileInfo) - => fileInfo.TryParseSerializableHeader(out var header) - ? header.Schema?.ToString() - : null; - - public static void ReadBuffer(this SerializableDocument doc, BFastBufferReader bufferReader) - { - var (name, numBytes) = bufferReader; - var stream = bufferReader.Seek(); - - switch (name) - { - case BufferNames.Header: - { - doc.Header = stream.ReadArray((int)numBytes).ToSerializableHeader(); - break; - } - - case BufferNames.Assets: - { - if (doc.Options?.SkipAssets == true) - break; - - doc.Assets = stream.ReadBFast().ToArray(); - break; - } - - case BufferNames.Strings: - { - doc.StringTable = ReadStrings(stream, numBytes); - break; - } - - case BufferNames.Geometry: - { - if (doc.Options?.SkipGeometry == true) - break; - - doc.Geometry = G3D.Read(stream); - break; - } - - case BufferNames.Entities: - { - doc.EntityTables = - bufferReader - .EnumerateEntityTables(doc.Options?.SchemaOnly ?? false) - .ToList(); - break; - } - } - } - - public static string[] ReadStrings(Stream stream, long numBytes) - { - var stringBytes = stream.ReadArray((int)numBytes); - var joinedStringTable = Encoding.UTF8.GetString(stringBytes); - return joinedStringTable.Split('\0'); - } - - public static SerializableDocument Deserialize(Stream stream, LoadOptions loadOptions = null) - { - var doc = new SerializableDocument { Options = loadOptions }; - - foreach (var buffer in stream.GetBFastBufferReaders()) - { - doc.ReadBuffer(buffer); - } - - return doc; - } - - public static SerializableDocument Deserialize(string filePath, LoadOptions loadOptions = null) - { - using (var stream = File.OpenRead(filePath)) - { - var doc = Deserialize(stream, loadOptions); - doc.SetFileName(filePath); - return doc; - } - } - } -} diff --git a/src/cs/vim/Vim.Format.Core/TableNameExtensions.cs b/src/cs/vim/Vim.Format.Core/TableNameExtensions.cs new file mode 100644 index 00000000..be6c2072 --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/TableNameExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Text.RegularExpressions; +using Vim.BFastLib; + +namespace Vim.Format +{ + public static class TableNameExtensions + { + private static readonly Regex IndexColumnNameComponentsRegex = new Regex(@"(\w+:)((?:\w|\.)+):(.+)"); + + public static string GetRelatedTableNameFromIndexColumnName(string indexColumnName) + { + var match = IndexColumnNameComponentsRegex.Match(indexColumnName); + if (!match.Success) + throw new Exception($"Index column name {indexColumnName} could not be separated into its components."); + + return match.Groups[2].Value; + } + + public static string GetRelatedTableName(this INamedBuffer ic) + => GetRelatedTableNameFromIndexColumnName(ic.Name); + + public static EntityTable GetRelatedTable(this INamedBuffer ic, Document doc) + => doc.GetTable(ic.GetRelatedTableName()); + } +} diff --git a/src/cs/vim/Vim.Format.Core/Validation.cs b/src/cs/vim/Vim.Format.Core/Validation.cs index bb5287ab..a25a941b 100644 --- a/src/cs/vim/Vim.Format.Core/Validation.cs +++ b/src/cs/vim/Vim.Format.Core/Validation.cs @@ -1,30 +1,27 @@ using System; -using System.Collections.Generic; -using Vim.BFast; -using Vim.G3d; -using Vim.LinqArray; +using Vim.BFastLib; namespace Vim.Format { public static class Validation { - public static void ValidateTableRows(this Document doc) + private static void ValidateTableRows(this Document doc) { - foreach (var et in doc.EntityTables.Values.ToArray()) + foreach (var et in doc.Tables) { - foreach (var c in et.IndexColumns.Values.ToArray()) + foreach (var c in et.IndexColumns) { if (c.Array.Length != et.NumRows) throw new Exception($"Expected array length {c.Array.Length} of column {c.Name} to be the same as number of rows {et.NumRows}"); } - foreach (var c in et.DataColumns.Values.ToArray()) + foreach (var c in et.DataColumns) { if (c.NumElements() != et.NumRows) throw new Exception($"Expected array length {c.NumElements()} of column {c.Name} to be the same as number of rows {et.NumRows}"); } - foreach (var c in et.StringColumns.Values.ToArray()) + foreach (var c in et.StringColumns) { if (c.Array.Length != et.NumRows) throw new Exception($"Expected array length {c.Array.Length} of column {c.Name} to be the same as number of rows {et.NumRows}"); @@ -32,11 +29,11 @@ public static void ValidateTableRows(this Document doc) } } - public static void ValidateIndexColumns(this Document doc) + private static void ValidateIndexColumns(this Document doc) { - foreach (var et in doc.EntityTables.Values.ToArray()) + foreach (var et in doc.Tables) { - foreach (var ic in et.IndexColumns.Values.ToEnumerable()) + foreach (var ic in et.IndexColumns) { var table = ic.GetRelatedTable(doc); if (table == null) @@ -45,37 +42,9 @@ public static void ValidateIndexColumns(this Document doc) } } - public static string[] RequiredAttributeNames => new [] + private static void ValidateAssets(this Document doc) { - // Vertices - CommonAttributes.Position, - CommonAttributes.Index, - - // Meshes - CommonAttributes.MeshSubmeshOffset, - - // Submeshes - CommonAttributes.SubmeshIndexOffset, - - // Instances - CommonAttributes.InstanceMesh, - CommonAttributes.InstanceTransform, - }; - - public static void ValidateGeometryAttributes(this Document doc) - { - var attributes = doc.Geometry.Attributes; - var attributeNameSet = new HashSet(attributes.Select(a => a.Name).ToEnumerable()); - foreach (var attributeName in RequiredAttributeNames) - { - if (!attributeNameSet.Contains(attributeName)) - throw new Exception($"Required attribute {attributeName} was not found."); - } - } - - public static void ValidateAssets(this Document doc) - { - foreach (var asset in doc.Assets.Values.ToEnumerable()) + foreach (var asset in doc.Assets.Values) AssetInfo.Parse(asset.Name); // This will throw if it fails to parse. } @@ -83,7 +52,6 @@ public static void Validate(this Document doc) { doc.ValidateTableRows(); doc.ValidateIndexColumns(); - doc.ValidateGeometryAttributes(); doc.ValidateAssets(); } diff --git a/src/cs/vim/Vim.Format.Core/Vim.Format.Core.csproj b/src/cs/vim/Vim.Format.Core/Vim.Format.Core.csproj index d845180e..a291acd7 100644 --- a/src/cs/vim/Vim.Format.Core/Vim.Format.Core.csproj +++ b/src/cs/vim/Vim.Format.Core/Vim.Format.Core.csproj @@ -7,11 +7,10 @@ - + + + - - - @@ -23,20 +22,11 @@ - - - TextTemplatingFileGenerator - ArrayOps.cs + + True + - - - True - True - ArrayOps.tt - - - diff --git a/src/cs/vim/Vim.Format.Core/VimConstants.cs b/src/cs/vim/Vim.Format.Core/VimConstants.cs index 72867a68..7b4c5e39 100644 --- a/src/cs/vim/Vim.Format.Core/VimConstants.cs +++ b/src/cs/vim/Vim.Format.Core/VimConstants.cs @@ -96,26 +96,16 @@ public static class VimConstants public const string BuildingParameterHolderElementType = "Building Parameter Holder"; public const string SiteParameterHolderElementType = "Site Parameter Holder"; - public static HashSet ComputedTableNames = new HashSet + public static readonly HashSet ComputedTableNames = new HashSet { TableNames.Geometry }; - public static HashSet NonBimNames = new HashSet + public static readonly HashSet NonBimNames = new HashSet { TableNames.Geometry, TableNames.Asset, TableNames.Material, }; - - public static class DisciplineNames - { - public const string Mechanical = nameof(Mechanical); - public const string Architecture = nameof(Architecture); - public const string Generic = nameof(Generic); - public const string Electrical = nameof(Electrical); - public const string Plumbing = nameof(Plumbing); - public const string Structural = nameof(Structural); - } } } diff --git a/src/cs/vim/Vim.Format.Core/VimFormatVersion.cs b/src/cs/vim/Vim.Format.Core/VimFormatVersion.cs index 2751a909..b0cecbb2 100644 --- a/src/cs/vim/Vim.Format.Core/VimFormatVersion.cs +++ b/src/cs/vim/Vim.Format.Core/VimFormatVersion.cs @@ -6,6 +6,5 @@ public static class VimFormatVersion { public static SerializableVersion Current => v1_0_0; public static SerializableVersion v1_0_0 => SerializableVersion.Parse("1.0.0"); - public static SerializableVersion v0_9_0 => SerializableVersion.Parse("0.9.0"); } } diff --git a/src/cs/vim/Vim.Format.Core/VimSchema.cs b/src/cs/vim/Vim.Format.Core/VimSchema.cs index a8a946e0..2906ed26 100644 --- a/src/cs/vim/Vim.Format.Core/VimSchema.cs +++ b/src/cs/vim/Vim.Format.Core/VimSchema.cs @@ -2,55 +2,57 @@ using System.Collections.Generic; using System.Linq; using Vim.Util; -using Vim.LinqArray; namespace Vim.Format { public class VimSchema { - public const string TableNameSeparator = "__"; + private const string TableNameSeparator = "__"; public readonly SerializableVersion VimFormatVersion; public readonly SerializableVersion SchemaVersion; - public readonly Dictionary EntityTableSchemas = new Dictionary(); + private readonly Dictionary _entityTableSchemas = new Dictionary(); public VimSchema(SerializableVersion vimFormatVersion, SerializableVersion schemaVersion) => (VimFormatVersion, SchemaVersion) = (vimFormatVersion, schemaVersion); - public VimSchema(SerializableHeader header) + private VimSchema(SerializableHeader header) : this(header.FileFormatVersion, header.Schema) { } public IEnumerable<(string TableName, string[] ColumnName)> GetTableNamesAndColumns() - => EntityTableSchemas.Values + => _entityTableSchemas.Values .Select(ets => (ets.TableName, ets.ColumnNames.OrderBy(n => n).ToArray())) .OrderBy(n => n); - public IEnumerable<(string TableName, string ColumnName)> GetFlattenedTableNamesAndColumnNames() - => EntityTableSchemas.Values.SelectMany(ets => ets.ColumnNames.Select(c => (ets.TableName, c))); + private IEnumerable<(string TableName, string ColumnName)> GetFlattenedTableNamesAndColumnNames() + => _entityTableSchemas.Values.SelectMany(ets => ets.ColumnNames.Select(c => (ets.TableName, c))); - public IEnumerable GetAllQualifiedColumnNames() + private IEnumerable GetAllQualifiedColumnNames() => GetFlattenedTableNamesAndColumnNames() .Select(t => string.Join(TableNameSeparator, t.TableName, t.ColumnName)) .OrderBy(x => x); public EntityTableSchema AddEntityTableSchema(string entityTableName) { - if (EntityTableSchemas.ContainsKey(entityTableName)) + if (_entityTableSchemas.ContainsKey(entityTableName)) throw new Exception($"Entity Table {entityTableName} already exists in the VIM schema"); var ets = new EntityTableSchema(entityTableName); - EntityTableSchemas.Add(entityTableName, ets); + _entityTableSchemas.Add(entityTableName, ets); return ets; } public static VimSchema Create(string filePath) - => Create(Serializer.Deserialize(filePath).ToDocument()); + => Create(SerializableDocument.FromPath(filePath, new LoadOptions() { SchemaOnly=true}) ); - public static VimSchema Create(Document doc) + public static VimSchema Create(SerializableDocument doc) + => Create(new Document(doc)); + + private static VimSchema Create(Document doc) { var vimSchema = new VimSchema(doc.Header); - foreach (var entityTable in doc.EntityTables.Values.ToEnumerable()) + foreach (var entityTable in doc.Tables) { var ets = vimSchema.AddEntityTableSchema(entityTable.Name); @@ -61,7 +63,7 @@ public static VimSchema Create(Document doc) return vimSchema; } - public VimSchemaDiff Diff(VimSchema other) + private VimSchemaDiff Diff(VimSchema other) { var aCols = GetAllQualifiedColumnNames().ToArray(); var bCols = other.GetAllQualifiedColumnNames().ToArray(); @@ -71,7 +73,7 @@ public VimSchemaDiff Diff(VimSchema other) public static VimSchemaDiff Diff(VimSchema a, VimSchema b) => a.Diff(b); - public bool IsSame(VimSchema other) + private bool IsSame(VimSchema other) { var diff = Diff(other); return diff.AddedQualifiedColumnNames.Length == 0 && diff.RemovedQualifiedColumnNames.Length == 0; diff --git a/src/cs/vim/Vim.Format.Tests/EntityTable_v2_Tests.cs b/src/cs/vim/Vim.Format.Tests/EntityTable_v2_Tests.cs index f9d9c28a..c111ac94 100644 --- a/src/cs/vim/Vim.Format.Tests/EntityTable_v2_Tests.cs +++ b/src/cs/vim/Vim.Format.Tests/EntityTable_v2_Tests.cs @@ -2,7 +2,6 @@ using System.IO; using System.Linq; using Vim.Format.ObjectModel; -using Vim.LinqArray; using Vim.Util.Tests; namespace Vim.Format.Tests; @@ -21,9 +20,8 @@ public static void TestEntityTable_v2_Parity() var dm = vim.DocumentModel; // EntityTable_v2 manual construction. - var fileInfo = new FileInfo(vimFilePath); - var entityTables = fileInfo.EnumerateEntityTables(false).ToArray(); - var entityTableSet = new EntityTableSet(entityTables, stringBuffer); + var entityTableSet = new EntityTableSet( + SerializableDocument.FromPath(vimFilePath).EntityTables.ToArray(), stringBuffer); var baseElementCount = dm.NumElement; var nextElementCount = entityTableSet.ElementTable.RowCount; diff --git a/src/cs/vim/Vim.Format.Tests/FormatTests.cs b/src/cs/vim/Vim.Format.Tests/FormatTests.cs index 15b20f90..8fffe617 100644 --- a/src/cs/vim/Vim.Format.Tests/FormatTests.cs +++ b/src/cs/vim/Vim.Format.Tests/FormatTests.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using Vim.BFast; -using Vim.LinqArray; +using Vim.BFastLib; +using Vim.Util; namespace Vim.Format.Tests { @@ -68,9 +68,9 @@ public static void AssertEquals(SerializableDocument d1, SerializableDocument d2 public static void AssertEquals(EntityTable et1, EntityTable et2) { Assert.AreEqual(et1.Name, et2.Name); - Assert.AreEqual(et1.DataColumns.Keys.ToEnumerable().OrderBy(n => n).ToArray(), et2.DataColumns.Keys.ToEnumerable().OrderBy(n => n).ToArray()); - Assert.AreEqual(et1.IndexColumns.Keys.ToEnumerable().OrderBy(n => n).ToArray(), et2.IndexColumns.Keys.ToEnumerable().OrderBy(n => n).ToArray()); - Assert.AreEqual(et1.StringColumns.Keys.ToEnumerable().OrderBy(n => n).ToArray(), et2.StringColumns.Keys.ToEnumerable().OrderBy(n => n).ToArray()); + Assert.AreEqual(et1.DataColumnNames.OrderBy(n => n).ToArray(), et2.DataColumnNames.OrderBy(n => n).ToArray()); + Assert.AreEqual(et1.IndexColumnNames.OrderBy(n => n).ToArray(), et2.IndexColumnNames.OrderBy(n => n).ToArray()); + Assert.AreEqual(et1.StringColumnNames.OrderBy(n => n).ToArray(), et2.StringColumnNames.OrderBy(n => n).ToArray()); var columns1 = et1.Columns.OrderBy(c => c.Name).ToArray(); var columns2 = et2.Columns.OrderBy(c => c.Name).ToArray(); @@ -104,9 +104,9 @@ public static bool IsSupersetOf(IEnumerable a, IEnumerable b) public static void AssertIsSupersetOf(EntityTable et1, EntityTable et2) { Assert.AreEqual(et1.Name, et2.Name); - Assert.IsTrue(IsSupersetOf(et1.DataColumns.Keys.ToEnumerable(), et2.DataColumns.Keys.ToEnumerable())); - Assert.IsTrue(IsSupersetOf(et1.IndexColumns.Keys.ToEnumerable(), et2.IndexColumns.Keys.ToEnumerable())); - Assert.IsTrue(IsSupersetOf(et1.StringColumns.Keys.ToEnumerable(), et2.StringColumns.Keys.ToEnumerable())); + Assert.IsTrue(IsSupersetOf(et1.DataColumnNames, et2.DataColumnNames)); + Assert.IsTrue(IsSupersetOf(et1.IndexColumnNames, et2.IndexColumnNames)); + Assert.IsTrue(IsSupersetOf(et1.StringColumnNames, et2.StringColumnNames)); var columns1 = et1.Columns.ToArray(); var columns2 = et2.Columns.ToArray(); @@ -131,21 +131,21 @@ public static void AssertIsSupersetOf(EntityTable et1, EntityTable et2) /// public static void AssertIsSuperSetOf(Document d1, Document d2, bool skipGeometryAndNodes = true) { - var schema1 = VimSchema.Create(d1); - var schema2 = VimSchema.Create(d2); + var schema1 = d1.GetSchema(); + var schema2 = d2.GetSchema(); Assert.IsTrue(VimSchema.IsSuperSetOf(schema1, schema2)); - var etKeys1 = d1.EntityTables.Keys; - var etKeys2 = d2.EntityTables.Keys; - Assert.IsTrue(IsSupersetOf(etKeys1.ToEnumerable(), etKeys2.ToEnumerable())); + var etKeys1 = d1.TableNames; + var etKeys2 = d2.TableNames; + Assert.IsTrue(IsSupersetOf(etKeys1, etKeys2)); - foreach (var key in etKeys2.ToEnumerable()) + foreach (var key in etKeys2) { if (skipGeometryAndNodes && key.ToLowerInvariant().Contains("geometry")) continue; - var et2 = d2.EntityTables[key]; - var et1 = d1.EntityTables.GetOrDefault(key); + var et2 = d2.GetTable(key); + var et1 = d1.GetTable(key); if (et1 == null) Assert.Fail($"No matching entity table found: {key}"); AssertIsSupersetOf(et1, et2); @@ -154,19 +154,19 @@ public static void AssertIsSuperSetOf(Document d1, Document d2, bool skipGeometr public static void AssertEquals(Document d1, Document d2, bool skipGeometryAndNodes = false) { - var schema1 = VimSchema.Create(d1); - var schema2 = VimSchema.Create(d2); + var schema1 = d1.GetSchema(); + var schema2 = d2.GetSchema(); Assert.IsTrue(VimSchema.IsSame(schema1, schema2)); - var entityTables1 = d1.EntityTables.Keys.ToEnumerable().OrderBy(n => n).ToArray(); - var entityTables2 = d2.EntityTables.Keys.ToEnumerable().OrderBy(n => n).ToArray(); + var entityTables1 = d1.TableNames.OrderBy(n => n).ToArray(); + var entityTables2 = d2.TableNames.OrderBy(n => n).ToArray(); Assert.AreEqual(entityTables1, entityTables2); foreach (var k in entityTables1) { if (skipGeometryAndNodes && k.ToLowerInvariant().Contains("geometry")) continue; - AssertEquals(d1.EntityTables[k], d2.EntityTables[k]); + AssertEquals(d1.GetTable(k), d2.GetTable(k)); } } } diff --git a/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs b/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs index 9efac292..003cd78e 100644 --- a/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs +++ b/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs @@ -1,119 +1,76 @@ -using System; +using NUnit.Framework; +using System; using System.Linq; -using NUnit.Framework; using Vim.Format.Geometry; using Vim.G3d; -using Vim.LinqArray; using Vim.Math3d; namespace Vim.Format.Tests.Geometry { public static class GeometryTests { - public static IMesh XYTriangle = new[] { new Vector3(0f, 0f, 0f), new Vector3(0f, 1f, 0f), new Vector3(1f, 0f, 0f) }.ToIArray().TriMesh(3.Range()); - public static IMesh XYQuad = new[] { new Vector3(0f, 0f, 0f), new Vector3(0f, 1f, 0f), new Vector3(1f, 1f, 0f), new Vector3(1f, 0f, 0f) }.ToIArray().QuadMesh(4.Range()); - public static IMesh XYQuadFromFunc = Primitives.QuadMesh(uv => uv.ToVector3(), 1, 1); - public static IMesh XYQuad2x2 = Primitives.QuadMesh(uv => uv.ToVector3(), 2, 2); - public static IMesh XYTriangleTwice = XYTriangle.Merge(XYTriangle.Translate(new Vector3(1, 0, 0))); - - public static readonly Vector3[] TestTetrahedronVertices = { Vector3.Zero, Vector3.UnitX, Vector3.UnitY, Vector3.UnitZ }; - public static readonly int[] TestTetrahedronIndices = { 0, 1, 2, 0, 3, 1, 1, 3, 2, 2, 3, 0 }; - - public static IMesh Tetrahedron = - TestTetrahedronVertices.ToIArray().TriMesh(TestTetrahedronIndices.ToIArray()); - - public static IMesh Torus = Primitives.Torus(10, 0.2f, 10, 24); - - static IMesh RevolvedVerticalCylinder(float height, float radius, int verticalSegments, int radialSegments) - => (Vector3.UnitZ * height).ToLine().Interpolate(verticalSegments).Add(-radius.AlongX()).RevolveAroundAxis(Vector3.UnitZ, radialSegments); - - public static IMesh Cylinder = RevolvedVerticalCylinder(5, 1, 4, 12); - - public static IMesh[] AllMeshes = { + public static VimMesh XYTriangle = new VimMesh( + new[] { 0, 1, 2 }, new[] { + new Vector3(0f, 0f, 0f), + new Vector3(0f, 1f, 0f), + new Vector3(1f, 0f, 0f) + }); + + public static VimMesh XYQuad = VimMesh.FromQuad( + new int[] { 0, 1, 2, 3 }, new[]{ + new Vector3(0f, 0f, 0f), + new Vector3(0f, 1f, 0f), + new Vector3(1f, 1f, 0f), + new Vector3(1f, 0f, 0f) + }); + + public static VimMesh XYQuadFromFunc = Primitives.QuadMesh(uv => uv.ToVector3(), 1, 1); + public static VimMesh XYQuad2x2 = Primitives.QuadMesh(uv => uv.ToVector3(), 2, 2); + public static VimMesh XYTriangleTwice = XYTriangle.Merge2(XYTriangle.Translate(new Vector3(1, 0, 0))); + + public static VimMesh Tetrahedron = new VimMesh( + new[] { 0, 1, 2, 0, 3, 1, 1, 3, 2, 2, 3, 0 }, new[] { + Vector3.Zero, + Vector3.UnitX, + Vector3.UnitY, + Vector3.UnitZ + }); + + public static VimMesh Torus = Primitives.Torus(10, 0.2f, 10, 24); + + public static VimMesh[] AllMeshes = { XYTriangle, // 0 XYQuad, // 1 XYQuadFromFunc, // 2 XYQuad2x2, // 3 Tetrahedron, // 4 Torus, // 5 - Cylinder, // 6 XYTriangleTwice, // 7 }; public static double SmallTolerance = 0.0001; - public static void AssertEquals(IMesh g1, IMesh g2) + public static void AssertEquals(VimMesh g1, VimMesh g2) { Assert.AreEqual(g1.NumFaces, g2.NumFaces); Assert.AreEqual(g1.NumCorners, g2.NumCorners); Assert.AreEqual(g1.NumVertices, g2.NumVertices); - for (var i = 0; i < g1.Indices.Count; i++) + for (var i = 0; i < g1.indices.Length; i++) { - var v1 = g1.Vertices[g1.Indices[i]]; - var v2 = g2.Vertices[g2.Indices[i]]; + var v1 = g1.vertices[g1.indices[i]]; + var v2 = g2.vertices[g2.indices[i]]; Assert.IsTrue(v1.AlmostEquals(v2, (float)SmallTolerance)); } } - public static void AssertEqualsWithAttributes(IMesh g1, IMesh g2) - { - AssertEquals(g1, g2); - - Assert.AreEqual( - g1.Attributes.Select(attr => attr.Name).ToArray(), - g2.Attributes.Select(attr => attr.Name).ToArray()); - - Assert.AreEqual( - g1.Attributes.Select(attr => attr.ElementCount).ToArray(), - g2.Attributes.Select(attr => attr.ElementCount).ToArray()); - } - - public static void OutputTriangleStats(Triangle t) - { - Console.WriteLine($"Vertices: {t.A} {t.B} {t.C}"); - Console.WriteLine($"Area: {t.Area} Perimeter: {t.Perimeter} Midpoint: {t.MidPoint}"); - Console.WriteLine($"Bounding box: {t.BoundingBox}"); - Console.WriteLine($"Bounding sphere: {t.BoundingSphere}"); - Console.WriteLine($"Normal: {t.Normal}, normal direction {t.NormalDirection}"); - Console.WriteLine($"Lengths: {t.LengthA} {t.LengthB} {t.LengthC}"); - } - - public static void OutputTriangleStatsSummary(IMesh g) - { - var triangles = g.Triangles(); - for (var i = 0; i < Math.Min(3, triangles.Count); ++i) - { - Console.WriteLine($"Triangle {i}"); - OutputTriangleStats(triangles[i]); - } - - if (triangles.Count > 3) - { - Console.WriteLine("..."); - Console.WriteLine($"Triangle {triangles.Count - 1}"); - OutputTriangleStats(triangles.Last()); - } - } - - public static void OutputIMeshStats(IMesh g) + public static void GeometryNullOps(VimMesh g) { - g.Validate(); - foreach (var attr in g.Attributes.ToEnumerable()) - Console.WriteLine($"{attr.Descriptor} elementCount={attr.ElementCount}"); + g.GeometryEquals(g); + g.Translate(Vector3.Zero).GeometryEquals(g); + g.Scale(Vector3.Zero).GeometryEquals(g); + g.Transform(Matrix4x4.Identity).GeometryEquals(g); } - public static void GeometryNullOps(IMesh g) - { - AssertEqualsWithAttributes(g, g); - AssertEqualsWithAttributes(g, g.Attributes.ToIMesh()); - AssertEqualsWithAttributes(g, g.Translate(Vector3.Zero)); - AssertEqualsWithAttributes(g, g.Scale(1.0f)); - AssertEqualsWithAttributes(g, g.Transform(Matrix4x4.Identity)); - - AssertEquals(g, g.CopyFaces(0, g.NumFaces).ToIMesh()); - } - - [Test] public static void BasicTests() { @@ -127,45 +84,42 @@ public static void BasicTests() Assert.AreEqual(3, XYTriangle.NumCornersPerFace); Assert.AreEqual(1, XYTriangle.NumFaces); - Assert.AreEqual(3, XYTriangle.Vertices.Count); - Assert.AreEqual(3, XYTriangle.Indices.Count); - Assert.AreEqual(1, XYTriangle.Triangles().Count); - Assert.AreEqual(0.5, XYTriangle.Area(), SmallTolerance); + Assert.AreEqual(3, XYTriangle.vertices.Length); + Assert.AreEqual(3, XYTriangle.indices.Length); + Assert.AreEqual(1, XYTriangle.Triangles().Length); Assert.IsTrue(XYTriangle.Planar()); - Assert.AreEqual(new[] { 0, 1, 2 }, XYTriangle.Indices.ToArray()); + Assert.AreEqual(new[] { 0, 1, 2 }, XYTriangle.indices.Length); Assert.AreEqual(3, XYQuad.NumCornersPerFace); Assert.AreEqual(2, XYQuad.NumFaces); - Assert.AreEqual(4, XYQuad.Vertices.Count); - Assert.AreEqual(6, XYQuad.Indices.Count); + Assert.AreEqual(4, XYQuad.vertices.Length); + Assert.AreEqual(6, XYQuad.indices.Length); Assert.IsTrue(XYQuad.Planar()); - Assert.AreEqual(new[] { 0, 1, 2, 0, 2, 3 }, XYQuad.Indices.ToArray()); + Assert.AreEqual(new[] { 0, 1, 2, 0, 2, 3 }, XYQuad.indices.ToArray()); Assert.AreEqual(3, XYQuadFromFunc.NumCornersPerFace); Assert.AreEqual(2, XYQuadFromFunc.NumFaces); - Assert.AreEqual(4, XYQuadFromFunc.Vertices.Count); - Assert.AreEqual(6, XYQuadFromFunc.Indices.Count); + Assert.AreEqual(4, XYQuadFromFunc.vertices.Length); + Assert.AreEqual(6, XYQuadFromFunc.indices.Length); Assert.AreEqual(3, XYQuad2x2.NumCornersPerFace); Assert.AreEqual(8, XYQuad2x2.NumFaces); - Assert.AreEqual(9, XYQuad2x2.Vertices.Count); - Assert.AreEqual(24, XYQuad2x2.Indices.Count); + Assert.AreEqual(9, XYQuad2x2.vertices.Length); + Assert.AreEqual(24, XYQuad2x2.indices.Length); Assert.AreEqual(3, Tetrahedron.NumCornersPerFace); Assert.AreEqual(4, Tetrahedron.NumFaces); - Assert.AreEqual(4, Tetrahedron.Vertices.Count); - Assert.AreEqual(12, Tetrahedron.Indices.Count); - Assert.AreEqual(TestTetrahedronIndices, Tetrahedron.Indices.ToArray()); + Assert.AreEqual(4, Tetrahedron.vertices.Length); + Assert.AreEqual(12, Tetrahedron.indices.Length); Assert.AreEqual(3, XYTriangleTwice.NumCornersPerFace); Assert.AreEqual(2, XYTriangleTwice.NumFaces); - Assert.AreEqual(6, XYTriangleTwice.Vertices.Count); - Assert.AreEqual(6, XYTriangleTwice.Indices.Count); - Assert.AreEqual(2, XYTriangleTwice.Triangles().Count); - Assert.AreEqual(1.0, XYTriangleTwice.Area(), SmallTolerance); + Assert.AreEqual(6, XYTriangleTwice.vertices.Length); + Assert.AreEqual(6, XYTriangleTwice.indices.Length); + Assert.AreEqual(2, XYTriangleTwice.Triangles().Length); Assert.IsTrue(XYTriangleTwice.Planar()); - Assert.AreEqual(new[] { 0, 1, 2, 3, 4, 5 }, XYTriangleTwice.Indices.ToArray()); + Assert.AreEqual(new[] { 0, 1, 2, 3, 4, 5 }, XYTriangleTwice.indices); } [Test] @@ -182,16 +136,16 @@ public static void OutputGeometryData() foreach (var g in AllMeshes) { Console.WriteLine($"Geometry {n++}"); - for (var i = 0; i < g.Vertices.Count && i < 10; ++i) + for (var i = 0; i < g.vertices.Length && i < 10; ++i) { - Console.WriteLine($"Vertex {i} {g.Vertices[i]}"); + Console.WriteLine($"Vertex {i} {g.vertices[i]}"); } - if (g.Vertices.Count > 10) + if (g.vertices.Length > 10) { - var last = g.Vertices.Count - 1; + var last = g.vertices.Length - 1; Console.WriteLine("..."); - Console.WriteLine($"Vertex {last} {g.Vertices[last]}"); + Console.WriteLine($"Vertex {last} {g.vertices[last]}"); } for (var i = 0; i < g.NumFaces && i < 10; ++i) @@ -199,7 +153,7 @@ public static void OutputGeometryData() Console.WriteLine($"Face {i}: {g.Triangle(i)}"); } - if (g.Vertices.Count > 10) + if (g.vertices.Length > 10) { var last = g.NumFaces - 1; Console.WriteLine("..."); @@ -212,22 +166,22 @@ public static void OutputGeometryData() public static void StripIndicesTests() { var emptyStrip00 = Primitives.QuadMeshStripIndicesFromPointRows(0, 0); - Assert.AreEqual(0, emptyStrip00.Count); + Assert.AreEqual(0, emptyStrip00.Length); var emptyStrip01 = Primitives.QuadMeshStripIndicesFromPointRows(0, 1); - Assert.AreEqual(0, emptyStrip01.Count); + Assert.AreEqual(0, emptyStrip01.Length); var emptyStrip10 = Primitives.QuadMeshStripIndicesFromPointRows(1, 0); - Assert.AreEqual(0, emptyStrip10.Count); + Assert.AreEqual(0, emptyStrip10.Length); var emptyStrip11 = Primitives.QuadMeshStripIndicesFromPointRows(1, 1); - Assert.AreEqual(0, emptyStrip11.Count); + Assert.AreEqual(0, emptyStrip11.Length); var emptyStrip12 = Primitives.QuadMeshStripIndicesFromPointRows(1, 2); - Assert.AreEqual(0, emptyStrip12.Count); + Assert.AreEqual(0, emptyStrip12.Length); var emptyStrip21 = Primitives.QuadMeshStripIndicesFromPointRows(2, 1); - Assert.AreEqual(0, emptyStrip21.Count); + Assert.AreEqual(0, emptyStrip21.Length); // COUNTER-CLOCKWISE TEST (DEFAULT) // 2------3 <--- row 1: [2,3] @@ -235,7 +189,7 @@ public static void StripIndicesTests() // | | // 0------1 <--- row 0: [0,1] var strip22 = Primitives.QuadMeshStripIndicesFromPointRows(2, 2); - Assert.AreEqual(4, strip22.Count); + Assert.AreEqual(4, strip22.Length); Assert.AreEqual(0, strip22[0]); Assert.AreEqual(1, strip22[1]); Assert.AreEqual(3, strip22[2]); @@ -247,13 +201,13 @@ public static void StripIndicesTests() // | | // 0------1 <--- row 0: [0,1] var clockwiseStrip22 = Primitives.QuadMeshStripIndicesFromPointRows(2, 2, true); - Assert.AreEqual(4, clockwiseStrip22.Count); + Assert.AreEqual(4, clockwiseStrip22.Length); Assert.AreEqual(2, clockwiseStrip22[0]); Assert.AreEqual(3, clockwiseStrip22[1]); Assert.AreEqual(1, clockwiseStrip22[2]); Assert.AreEqual(0, clockwiseStrip22[3]); - var reversed22 = clockwiseStrip22.ToIArray().Reverse(); - for (var i = 0; i < strip22.Count; ++i) + var reversed22 = clockwiseStrip22.Reverse().ToArray(); + for (var i = 0; i < strip22.Length; ++i) { Assert.AreEqual(strip22[i], reversed22[i]); } @@ -263,7 +217,7 @@ public static void StripIndicesTests() // | | | // *------*------* var strip23 = Primitives.QuadMeshStripIndicesFromPointRows(2, 3); - Assert.AreEqual(4 * 2, strip23.Count); + Assert.AreEqual(4 * 2, strip23.Length); // *------*------*------* // | | | | @@ -273,46 +227,36 @@ public static void StripIndicesTests() // | | | | // *------*------*------* var strip34 = Primitives.QuadMeshStripIndicesFromPointRows(3, 4); - Assert.AreEqual(4 * 6, strip34.Count); + Assert.AreEqual(4 * 6, strip34.Length); } [Test] public static void TriangleSerializationTest() { - // Serialize a triangle g3d to a bfast as bytes and read it back. - var vertices = new[] + //TODO: Check the need for this test. + // Serializing a triangle is that a use case ? + + var mesh = new VimMesh(new[] { 0, 1, 2 }, new[] { new Vector3(0, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 1) - }; - - var indices = new[] { 0, 1, 2 }; - var submeshIndexOffsets = new[] { 0 }; - var submeshMaterials = new[] { 0 }; - - var g3d = new G3DBuilder() - .AddVertices(vertices.ToIArray()) - .AddIndices(indices.ToIArray()) - .Add(submeshIndexOffsets.ToIArray().ToSubmeshIndexOffsetAttribute()) - .Add(submeshMaterials.ToIArray().ToSubmeshMaterialAttribute()) - .ToG3D(); - - var bfastBytes = g3d.WriteToBytes(); - var readG3d = G3D.Read(bfastBytes); - - Assert.IsNotNull(readG3d); - var mesh = readG3d.ToIMesh(); - mesh.Validate(); - - Assert.AreEqual(3, mesh.NumVertices); - Assert.AreEqual(new Vector3(0, 0, 0), mesh.Vertices[0]); - Assert.AreEqual(new Vector3(0, 1, 0), mesh.Vertices[1]); - Assert.AreEqual(new Vector3(0, 1, 1), mesh.Vertices[2]); - Assert.AreEqual(1, mesh.NumFaces); - Assert.AreEqual(0, mesh.SubmeshIndexOffsets.ToEnumerable().Single()); - Assert.AreEqual(0, mesh.SubmeshMaterials.ToEnumerable().Single()); - Assert.AreEqual(0, mesh.GetFaceMaterials().First()); + }); + + var bfast = mesh.ToG3d().ToBFast(); + var result = VimMesh.FromG3d(new G3dVim(bfast)); + + Assert.IsNotNull(mesh); + result.Validate(); + + Assert.AreEqual(3, result.NumVertices); + Assert.AreEqual(new Vector3(0, 0, 0), result.vertices[0]); + Assert.AreEqual(new Vector3(0, 1, 0), result.vertices[1]); + Assert.AreEqual(new Vector3(0, 1, 1), result.vertices[2]); + Assert.AreEqual(1, result.NumFaces); + Assert.AreEqual(0, result.submeshIndexOffsets.Single()); + Assert.AreEqual(-1, result.submeshMaterials.Single()); + Assert.AreEqual(-1, result.GetFaceMaterials().First()); } } } diff --git a/src/cs/vim/Vim.Format.Tests/Geometry/PerimeterTest.cs b/src/cs/vim/Vim.Format.Tests/Geometry/PerimeterTest.cs deleted file mode 100644 index 82f2ac6c..00000000 --- a/src/cs/vim/Vim.Format.Tests/Geometry/PerimeterTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -using NUnit.Framework; -using Vim.Format.Geometry; -using Vim.Math3d; - -namespace Vim.Format.Tests.Geometry -{ - public static class PerimeterTest - { - - [Test] - public static void Test() - { - var torus = Primitives.QuadMesh(uv => TorusFunction(uv, 10, 0.2f), 10, 24); - - var perimeter = torus.GeneratePerimeter(Vector3.UnitX); - } - - public static Vector3 TorusFunction(Vector2 uv, float radius, float tube) - { - uv = uv * Constants.TwoPi; - return new Vector3( - (radius + tube * uv.Y.Cos()) * uv.X.Cos(), - (radius + tube * uv.Y.Cos()) * uv.X.Sin(), - tube * uv.X.Sin()); - } - } -} diff --git a/src/cs/vim/Vim.Format.Tests/MergeTests.cs b/src/cs/vim/Vim.Format.Tests/MergeTests.cs index 417aa3a2..252cd0d3 100644 --- a/src/cs/vim/Vim.Format.Tests/MergeTests.cs +++ b/src/cs/vim/Vim.Format.Tests/MergeTests.cs @@ -6,7 +6,6 @@ using Vim.Format.Merge; using Vim.Format.ObjectModel; using Vim.Format.SceneBuilder; -using Vim.LinqArray; using Vim.Math3d; using Vim.Util; using Vim.Util.Tests; @@ -105,15 +104,15 @@ public static void TestMergeDifferentFilesAsGrid() mergedVim.Validate(); // The categories in the merged VIM must be a distinct count of the categories in vim1 and vim2. - var categoriesVim1 = vim1.DocumentModel.CategoryList.ToEnumerable(); - var categoriesVim2 = vim2.DocumentModel.CategoryList.ToEnumerable(); + var categoriesVim1 = vim1.DocumentModel.CategoryList; + var categoriesVim2 = vim2.DocumentModel.CategoryList; var distinctCategoryCount = categoriesVim1.Concat(categoriesVim2).Distinct(new CategoryEqualityComparer()).Count(); var mergedCategoryCount = mergedVim.DocumentModel.NumCategory; Assert.AreEqual(distinctCategoryCount, mergedCategoryCount); // The display units in the merged VIM must be a distinct count of the display units in vim1 and vim2. - var displayUnitsVim1 = vim1.DocumentModel.DisplayUnitList.ToEnumerable(); - var displayUnitsVim2 = vim2.DocumentModel.DisplayUnitList.ToEnumerable(); + var displayUnitsVim1 = vim1.DocumentModel.DisplayUnitList; + var displayUnitsVim2 = vim2.DocumentModel.DisplayUnitList; var distinctDisplayUnitCount = displayUnitsVim1.Concat(displayUnitsVim2).Distinct(new DisplayUnitEqualityComparer()).Count(); var mergedDisplayUnitCount = mergedVim.DocumentModel.NumDisplayUnit; Assert.AreEqual(distinctDisplayUnitCount, mergedDisplayUnitCount); diff --git a/src/cs/vim/Vim.Format.Tests/SerializableDocumentTests.cs b/src/cs/vim/Vim.Format.Tests/SerializableDocumentTests.cs new file mode 100644 index 00000000..392fd04b --- /dev/null +++ b/src/cs/vim/Vim.Format.Tests/SerializableDocumentTests.cs @@ -0,0 +1,174 @@ +using NUnit.Framework; +using Vim.Format.Geometry; +using Vim.Format.SceneBuilder; +using Vim.Util.Tests; + +namespace Vim.Format.Tests; + + + +[TestFixture] +public static class SerializableDocumentTests +{ + [Test] + public static void TestEmpty() + { + var doc = new SerializableDocument(); + Assert.DoesNotThrow(() => doc.ToBFast()); + } + + [Test] + public static void CanOpenVim() + { + var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + var scene = VimScene.LoadVim(path); + scene.Validate(); + } + + //[Test] + //public static void GetMesh_IsSameMesh() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + // for (var i = 0; i < scene.GetMeshCount(); i++) + // { + // var mesh = scene.MeshesOld[i]; + // var next = scene.MeshesNext[i]; + // var raw = scene.Meshes[i]; + // MeshesAreSame(mesh, next); + // MeshesAreSame(mesh, raw); + // } + //} + + //[Test] + //public static void GetMesh_Transform_IsSame() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + // for (var i = 0; i < scene.GetMeshCount(); i++) + // { + // var mat = Matrix4x4.CreateWorld( + // new Vector3(1, -2, 3), + // new Vector3(0, 0, 1), + // new Vector3(0, 1, 0) + // ); + // var mesh = scene.MeshesOld[i].Transform(mat); + // var next = scene.MeshesNext[i].Transform(mat); + // var raw = scene.Meshes[i].Transform(mat); + // MeshesAreSame(mesh, next); + // MeshesAreSame(mesh, raw); + // } + //} + + //[Test] + //public static void ReverseWindingOrder_IsSame() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + // for (var i = 0; i < scene.GetMeshCount(); i++) + // { + + // var mesh = scene.MeshesOld[i].ReverseWindingOrder() as VimMesh; + // var next = scene.MeshesNext[i].ReverseWindingOrder(); + // var raw = scene.Meshes[i].ReverseWindingOrder(); + // MeshesAreSame(mesh, next); + // MeshesAreSame(mesh, raw); + // } + //} + + //[Test] + //public static void FaceMaterial_IsSame() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + // for (var i = 0; i < scene.GetMeshCount(); i++) + // { + // var mesh = scene.MeshesOld[i].GetFaceMaterials(); + // var next = scene.MeshesNext[i].GetFaceMaterials(); + // var raw = scene.Meshes[i].GetFaceMaterials(); + // Assert.That(mesh.SequenceEquals(next)); + // Assert.That(next.SequenceEquals(raw)); + // } + //} + + //[Test] + //public static void Merge_ByPair_IsSame() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + // for (var i = 1; i < scene.GetMeshCount(); i++) + // { + // var mesh = scene.MeshesOld[i].Merge(scene.MeshesOld[i - 1]); + // var next = scene.MeshesNext[i].Merge2(scene.MeshesNext[i - 1]); + // var raw = scene.Meshes[i].Merge2(scene.Meshes[i - 1]); + // MeshesAreSame(mesh, next); + // MeshesAreSame(mesh, raw); + // } + //} + + //[Test] + //public static void FromG3d_Equals_ToVimMesh() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + // var mesh = scene.Document.Geometry as VimMesh; + // var next = VimMesh.FromG3d(scene.Document.GeometryNext); + // MeshesAreSame(mesh, next); + + //} + + //[Test] + //public static void Merge_All_IsSame() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + + // var mesh = scene.MeshesOld[0].Merge(scene.MeshesOld.Skip(1).ToArray()); + // var next = scene.MeshesNext[0].Merge2(scene.MeshesNext.Skip(1).ToArray()); + // var raw = scene.Meshes[0].Merge2(scene.Meshes.Skip(1).ToArray()); + // MeshesAreSame(mesh, next); + // MeshesAreSame(mesh, raw); + //} + + //[Test] + //public static void SplitByMaterials_IsSame() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + // for (var i = 0; i < scene.GetMeshCount(); i++) + // { + // var mesh = scene.MeshesOld[i].SplitByMaterial().ToArray(); + // var next = scene.MeshesNext[i].SplitByMaterial(); + // var raw = scene.Meshes[i].SplitByMaterial(); + // Assert.AreEqual(mesh.Length, next.Length); + // Assert.AreEqual(mesh.Length, raw.Length); + + // for (var j = 0; j < mesh.Length; j++) + // { + // MeshesAreSame(mesh[j].Mesh, next[j].mesh); + // MeshesAreSame(mesh[j].Mesh, raw[j].mesh); + // } + // } + //} + + //private static void Consume(VimMesh mesh) + //{ + // mesh.Indices.Sum(); + // mesh.Vertices.Sum(v => v.X); + // mesh.SubmeshIndexOffsets.Sum(); + // mesh.SubmeshIndexCount.Sum(); + // mesh.SubmeshMaterials.Sum(); + //} + + private static void MaterialsAreSame(IMaterial mesh, VimMaterialNext next) + { + Assert.AreEqual(mesh.Color, next.Color); + Assert.AreEqual(mesh.Glossiness, next.Glossiness); + Assert.AreEqual(mesh.Smoothness, next.Smoothness); + } + + + + +} + diff --git a/src/cs/vim/Vim.Format.Tests/TrainingSetTests.cs b/src/cs/vim/Vim.Format.Tests/TrainingSetTests.cs index 55981164..f4bbf810 100644 --- a/src/cs/vim/Vim.Format.Tests/TrainingSetTests.cs +++ b/src/cs/vim/Vim.Format.Tests/TrainingSetTests.cs @@ -5,7 +5,6 @@ using System.Globalization; using System.IO; using System.Linq; -using Vim.LinqArray; using Vim.Format.ObjectModel; using Vim.Util; using Vim.Util.Tests; diff --git a/src/cs/vim/Vim.Format.Tests/Vim.Format.Tests.csproj b/src/cs/vim/Vim.Format.Tests/Vim.Format.Tests.csproj index d26c5621..afad46c3 100644 --- a/src/cs/vim/Vim.Format.Tests/Vim.Format.Tests.csproj +++ b/src/cs/vim/Vim.Format.Tests/Vim.Format.Tests.csproj @@ -17,9 +17,14 @@ - - - + + + + + + True + + diff --git a/src/cs/vim/Vim.Format.Vimx.Conversion/Chunking.cs b/src/cs/vim/Vim.Format.Vimx.Conversion/Chunking.cs new file mode 100644 index 00000000..8ddf6187 --- /dev/null +++ b/src/cs/vim/Vim.Format.Vimx.Conversion/Chunking.cs @@ -0,0 +1,198 @@ +using System.Collections.Generic; +using System.Linq; +using Vim.Math3d; +using Vim.G3d; + +namespace Vim.Format.VimxLib.Conversion +{ + public static class Chunking + { + public static VimChunks CreateChunks(ChunksDescription description) + { + var chunks = new G3dChunk[description.ChunkMeshes.Count]; + for (var i = 0; i < chunks.Length; i++) + { + var meshes = description.ChunkMeshes[i]; + chunks[i] = CreateChunk(description.g3d, meshes); + } + return new VimChunks(description, chunks); + } + + public static ChunksDescription ComputeChunks(MeshOrder ordering) + { + // 2MB once compressed -> 0.5MB + const int ChunkSize = 2000000; + + var meshChunks = new int[ordering.Meshes.Length]; + var meshIndex = new int[ordering.Meshes.Length]; + + var chunks = new List>(); + var chunk = new List(); + var chunkSize = 0L; + for (var i = 0; i < ordering.Meshes.Length; i++) + { + var mesh = ordering.Meshes[i]; + chunkSize += ordering.g3d.GetApproxSize(mesh); + if (chunkSize > ChunkSize && chunk.Count > 0) + { + chunks.Add(chunk); + chunk = new List(); + chunkSize = 0; + } + + meshChunks[i] = chunks.Count; + meshIndex[i] = chunk.Count; + chunk.Add(mesh); + } + if (chunk.Count > 0) + { + chunks.Add(chunk); + } + + return new ChunksDescription(ordering, meshChunks, meshIndex, chunks); + } + + public class SubmeshBuffer + { + int index = 0; + public int[] IndexOffsets; + public int[] VertexOffsets; + public int[] Materials; + + public SubmeshBuffer(int count) + { + IndexOffsets = new int[count]; + VertexOffsets = new int[count]; + Materials = new int[count]; + } + + public void Add(int indexOffset, int vertexOffset, int material) + { + IndexOffsets[index] = indexOffset; + VertexOffsets[index] = vertexOffset; + Materials[index] = material; + index++; + } + } + + public class PointsBuffer + { + public int[] indices; + public List vertices; + + public int IndexCount { get; private set; } = 0; + public int VertexCount => vertices.Count; + + public PointsBuffer(int indexCount, int vertexCount) + { + indices = new int[indexCount]; + vertices = new List(vertexCount); + } + + public void AddIndex(int index) + { + indices[IndexCount++] = index; + } + + public void AddVertex(Vector3 vertex) + { + vertices.Add(vertex); + } + } + + public static G3dChunk CreateChunk(G3dVim g3d, List meshes) + { + var meshSubmeshOffsets = new int[meshes.Count + 1]; + var meshOpaqueCounts = new int[meshes.Count]; + + var submeshCount = meshes.Sum(m => g3d.GetMeshSubmeshCount(m)); + var submeshBuffer = new SubmeshBuffer(submeshCount); + + var indexCount = meshes.Sum(m => g3d.GetMeshIndexCount(m)); + var vertexCount = meshes.Sum(m => g3d.GetMeshVertexCount(m)); + var pointsBuffer = new PointsBuffer(indexCount, vertexCount); + + for (var i = 0; i < meshes.Count; i++) + { + var mesh = meshes[i]; + + var opaqueCount = AppendSubmeshes( + g3d, + mesh, + false, + submeshBuffer, + pointsBuffer + ); + + var transparentCount = AppendSubmeshes( + g3d, + mesh, + true, + submeshBuffer, + pointsBuffer + ); + meshOpaqueCounts[i] = opaqueCount; + meshSubmeshOffsets[i + 1] = meshSubmeshOffsets[i] + opaqueCount + transparentCount; + } + + return new G3dChunk( + meshSubmeshOffset: meshSubmeshOffsets, + meshOpaqueSubmeshCounts: meshOpaqueCounts, + submeshIndexOffsets: submeshBuffer.IndexOffsets, + submeshVertexOffsets :submeshBuffer.VertexOffsets, + submeshMaterials: submeshBuffer.Materials, + indices: pointsBuffer.indices, + positions: pointsBuffer.vertices.ToArray() + ); + } + + private static int AppendSubmeshes( + G3dVim g3d, + int mesh, + bool transparent, + SubmeshBuffer submeshBuffer, + PointsBuffer pointsBuffer + ) + { + var subStart = g3d.GetMeshSubmeshStart(mesh); + var subEnd = g3d.GetMeshSubmeshEnd(mesh); + var count = 0; + for (var sub = subStart; sub < subEnd; sub++) + { + var currentMat = g3d.SubmeshMaterials[sub]; + var color = currentMat > 0 ? g3d.MaterialColors[currentMat] : Vector4.One; + var accept = color.W < 1 == transparent; + + if (!accept) continue; + count++; + submeshBuffer.Add(pointsBuffer.IndexCount, pointsBuffer.VertexCount, currentMat); + g3d.GetSubmesh(sub, pointsBuffer); + } + return count; + } + + private static void GetSubmesh(this G3dVim g3d, int submesh, PointsBuffer points) + { + var index = points.VertexCount; + var dict = new Dictionary(); + var start = g3d.GetSubmeshIndexStart(submesh); + var end = g3d.GetSubmeshIndexEnd(submesh); + + for (var i = start; i < end; i++) + { + var v = g3d.Indices[i]; + if (dict.ContainsKey(v)) + { + points.AddIndex(dict[v]); + } + else + { + points.AddIndex(index); + points.AddVertex(g3d.Positions[v]); + dict.Add(v, index); + index++; + } + } + } + } +} diff --git a/src/cs/vim/Vim.Format.Vimx.Conversion/Ordering.cs b/src/cs/vim/Vim.Format.Vimx.Conversion/Ordering.cs new file mode 100644 index 00000000..587bac7e --- /dev/null +++ b/src/cs/vim/Vim.Format.Vimx.Conversion/Ordering.cs @@ -0,0 +1,78 @@ +using System; +using Vim.Format.ObjectModel; +using Vim.G3d; + +namespace Vim.Format.VimxLib.Conversion +{ + public static class Ordering + { + public static MeshOrder ComputeOrder(G3dVim g3d, DocumentModel bim) + { + var meshCount = g3d.GetMeshCount(); + var resultCount = 0; + for (var mesh = 0; mesh < meshCount; mesh++) + { + if (g3d.GetMeshInstances(mesh).Count > 0) resultCount++; + } + + var i = 0; + var order = new int[resultCount]; + var instanceCount = 0; + for (var mesh = 0; mesh < meshCount; mesh++) + { + var instances = g3d.GetMeshInstances(mesh); + if (instances.Count > 0) + { + instanceCount += instances.Count; + order[i++] = mesh; + } + } + Array.Sort(order, (a, b) => + { + var prioA = GetPriority(GetMeshName(g3d, bim, a)); + var prioB = GetPriority(GetMeshName(g3d, bim, b)); + return prioA - prioB; + }); + return new MeshOrder(g3d, order, instanceCount); + } + + static string GetMeshName(G3dVim g3d, DocumentModel bim, int mesh) + { + var node = g3d.GetMeshInstances(mesh)[0]; + + if (node < 0 || node >= bim.NodeElementIndex.Length) return ""; + var element = bim.NodeElementIndex[node]; + + if (element < 0 || element >= bim.ElementCategoryIndex.Length) return ""; + var category = bim.ElementCategoryIndex[element]; + + if (category < 0 || category >= bim.CategoryName.Length) return ""; + var name = bim.CategoryName[category]; + + return name; + } + + static int GetPriority(string value) + { + if (string.IsNullOrWhiteSpace(value)) return 0; + + if (value.Contains("Topography")) return 110; + if (value.Contains("Floor")) return 100; + if (value.Contains("Slab")) return 100; + if (value.Contains("Ceiling")) return 90; + if (value.Contains("Roof")) return 90; + + if (value.Contains("Curtain")) return 80; + if (value.Contains("Wall")) return 80; + if (value.Contains("Window")) return 70; + + if (value.Contains("Column")) return 60; + if (value.Contains("Structural")) return 60; + + if (value.Contains("Stair")) return 40; + if (value.Contains("Doors")) return 30; + + return 1; + } + } +} diff --git a/src/cs/vim/Vim.Format.Vimx.Conversion/Vim.Format.Vimx.Conversion.csproj b/src/cs/vim/Vim.Format.Vimx.Conversion/Vim.Format.Vimx.Conversion.csproj new file mode 100644 index 00000000..05a0161b --- /dev/null +++ b/src/cs/vim/Vim.Format.Vimx.Conversion/Vim.Format.Vimx.Conversion.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + + + + + + + + + + True + + + + + diff --git a/src/cs/vim/Vim.Format.Vimx.Conversion/VimxConverter.cs b/src/cs/vim/Vim.Format.Vimx.Conversion/VimxConverter.cs new file mode 100644 index 00000000..b233e655 --- /dev/null +++ b/src/cs/vim/Vim.Format.Vimx.Conversion/VimxConverter.cs @@ -0,0 +1,224 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Vim.Format.ObjectModel; +using Vim.G3d; +using Vim.Math3d; +using Vim.Util; + +namespace Vim.Format.VimxLib.Conversion +{ + public static class VimxConverter + { + public static Vimx FromVimPath(string vimPath) + { + var vim = VimScene.LoadVim(vimPath, new LoadOptions() + { + SkipAssets = true, + SkipGeometry = true, + }); + + var g3d = G3dVim.FromVim(vimPath); + var vimx = ConvertFromVim(g3d, vim.DocumentModel); + + return vimx; + } + + public static Vimx ConvertFromVim(G3dVim g3d, DocumentModel bim) + { + // Split input Vim into chunks. + var chunks = CreateChunks(g3d, bim); + + // Compute the scene definition from chunks. + var scene = CreateScene(chunks, bim); + + // Materials are reused from input g3d. + var materials = new G3dMaterials(g3d); + + var header = VimxHeader.CreateDefault(); + + return new Vimx(header, MetaHeader.Default, scene, materials, chunks.Chunks); + } + + public static VimChunks CreateChunks(G3dVim g3d, DocumentModel bim) + { + // First compute a desirable presentation order. + var ordering = Ordering.ComputeOrder(g3d, bim); + + // Groups meshes up to a certain size. + var groups = Chunking.ComputeChunks(ordering); + // Append and merge geometry from g3d to create the chunks. + + var chunks = Chunking.CreateChunks(groups); + + return chunks; + } + + public static G3dScene CreateScene(VimChunks chunks, DocumentModel bim) + { + var nodeElements = bim.NodeElementIndex.ToArray(); + + var instanceCount = chunks.InstanceCount; + var instanceNodes = new int[instanceCount]; + var instanceMeshes = new int[instanceCount]; + var instanceGroups = new int[instanceCount]; + var instanceTransforms = new Matrix4x4[instanceCount]; + var instanceFlags = new ushort[instanceCount]; + var instanceTags = new long[instanceCount]; + var instanceMins = new Vector3[instanceCount]; + var instanceMaxs = new Vector3[instanceCount]; + + var meshCount = chunks.MeshCount; + var indexCounts = new int[meshCount]; + var vertexCounts = new int[meshCount]; + var opaqueIndexCounts = new int[meshCount]; + var opaqueVertexCounts = new int[meshCount]; + + var sw = Stopwatch.StartNew(); + sw.Stop(); + var instance = 0; + for (var i = 0; i < meshCount; i++) + { + var meshChunk = chunks.MeshChunks[i]; + var meshIndex = chunks.MeshIndex[i]; + var instances = chunks.GetInstances(i); + var chunk = chunks.Chunks[meshChunk]; + for (var j = 0; j < instances.Count; j++) + { + var node = instances[j]; + var element = nodeElements[node]; + var transform = chunks.g3d.InstanceTransforms[node]; + + // geometry + instanceMeshes[instance] = i; + instanceTransforms[instance] = transform; + + // bounding box + sw.Start(); + var box = chunk.GetAABox(meshIndex, transform); + sw.Stop(); + instanceMins[instance] = box.Min; + instanceMaxs[instance] = box.Max; + + // bim + instanceNodes[instance] = node; + instanceGroups[instance] = element; + instanceTags[instance] = bim.ElementId.ElementAtOrDefault(element, -1); + + instance++; + } + + // geometry counts + indexCounts[i] = chunk.GetMeshIndexCount(meshIndex, MeshSection.All); + vertexCounts[i] = chunk.GetMeshVertexCount(meshIndex, MeshSection.All); + opaqueIndexCounts[i] = chunk.GetMeshIndexCount(meshIndex, MeshSection.Opaque); + opaqueVertexCounts[i] = chunk.GetMeshVertexCount(meshIndex, MeshSection.Opaque); ; + } + + // InstanceFlags is not always present. + if (chunks.g3d.InstanceFlags != null) + { + for(var i = 0; i < instanceNodes.Length; i++) + { + var node = instanceNodes[i]; + instanceFlags[i] = chunks.g3d.InstanceFlags[node]; + } + } + + var scene = new G3dScene( + + chunkCount: new[] { chunks.ChunkCount}, + instanceMeshes : instanceMeshes, + instanceTransformData: instanceTransforms, + instanceNodes: instanceNodes, + instanceFlags: instanceFlags, + instanceGroups: instanceGroups, + instanceMaxs: instanceMaxs, + instanceMins: instanceMins, + instanceTags: instanceTags, + meshChunks: chunks.MeshChunks, + meshChunkIndices: chunks.MeshIndex, + meshIndexCounts : indexCounts, + meshVertexCounts: vertexCounts, + meshOpaqueIndexCounts: opaqueIndexCounts, + meshOpaqueVertexCounts: opaqueVertexCounts + ); + return scene; + } + } + + /// + /// Initial step of vim->vimx conversion. + /// + public class MeshOrder + { + public readonly G3dVim g3d; + public readonly int[] Meshes; + public readonly int InstanceCount; + + public MeshOrder(G3dVim g3d, int[] meshes, int instanceCount) + { + this.g3d = g3d; + Meshes = meshes; + InstanceCount = instanceCount; + } + } + + /// + /// Describes how the meshes from the vim will be grouped in the vimx. + /// + public class ChunksDescription + { + public readonly G3dVim g3d; + public readonly int[] Meshes; + public readonly int[] MeshChunks; + public readonly int[] MeshIndex; + public readonly List> ChunkMeshes; + public readonly int InstanceCount; + + public ChunksDescription(MeshOrder ordering, int[] meshChunks, int[] meshIndex, List> chunkMeshes) + { + g3d = ordering.g3d; + Meshes = ordering.Meshes; + InstanceCount = ordering.InstanceCount; + MeshChunks = meshChunks; + MeshIndex = meshIndex; + ChunkMeshes = chunkMeshes; + } + } + + /// + /// Resulting Chunks of the vim->vimx conversion. + /// + public class VimChunks + { + public readonly G3dVim g3d; + public readonly int[] Meshes; + public readonly int[] MeshChunks; + public readonly int[] MeshIndex; + public readonly List> ChunkMeshes; + public readonly G3dChunk[] Chunks; + public readonly int InstanceCount; + + public VimChunks(ChunksDescription description, G3dChunk[] chunks) + { + g3d = description.g3d; + Meshes = description.Meshes; + MeshChunks = description.MeshChunks; + MeshIndex = description.MeshIndex; + ChunkMeshes = description.ChunkMeshes; + InstanceCount = description.InstanceCount; + Chunks = chunks; + } + + public int ChunkCount => Chunks.Length; + + public int MeshCount => Meshes.Length; + + public IReadOnlyList GetInstances(int meshIndex) + { + var m = Meshes[meshIndex]; + return g3d.GetMeshInstances(m); + } + } +} diff --git a/src/cs/vim/Vim.Format.Vimx/Vim.Format.Vimx.csproj b/src/cs/vim/Vim.Format.Vimx/Vim.Format.Vimx.csproj new file mode 100644 index 00000000..b9896ad8 --- /dev/null +++ b/src/cs/vim/Vim.Format.Vimx/Vim.Format.Vimx.csproj @@ -0,0 +1,23 @@ + + + + netstandard2.0 + + + + + + + + + + + + + + True + + + + + diff --git a/src/cs/vim/Vim.Format.Vimx/Vimx.cs b/src/cs/vim/Vim.Format.Vimx/Vimx.cs new file mode 100644 index 00000000..31c3c20e --- /dev/null +++ b/src/cs/vim/Vim.Format.Vimx/Vimx.cs @@ -0,0 +1,77 @@ +using System.Linq; +using Vim.BFastLib; +using Vim.G3d; + +namespace Vim.Format.VimxLib +{ + public static class BufferNames + { + public const string Header = "header"; + public const string Meta = "meta"; + public const string Scene = "scene"; + public const string Materials = "materials"; + public static string Chunk(int index) => $"chunk_{index}"; + } + + public static class BufferCompression + { + public const bool Scene = true; + public const bool Materials = true; + public const bool Chunks = true; + } + + public class Vimx + { + public readonly SerializableHeader Header; + public readonly MetaHeader Meta; + public readonly G3dScene Scene; + public readonly G3dMaterials Materials; + public readonly G3dChunk[] Chunks; + + public Vimx(SerializableHeader header, MetaHeader meta, G3dScene scene, G3dMaterials materials, G3dChunk[] chunks) + { + Meta = meta; + Header = header; + Scene = scene; + Materials = materials; + Chunks = chunks; + } + + public Vimx(BFast bfast) + { + Header = VimxHeader.FromBytes(bfast.GetArray(BufferNames.Header)); + + Scene = new G3dScene( + bfast.GetBFast(BufferNames.Scene, BufferCompression.Scene) + ); + + Materials = new G3dMaterials( + bfast.GetBFast(BufferNames.Materials, BufferCompression.Materials) + ); + + Chunks = Enumerable.Range(0, Scene.GetChunksCount()) + .Select(c => bfast.GetBFast(BufferNames.Chunk(c), BufferCompression.Chunks)) + .Select(b => new G3dChunk(b)) + .ToArray(); + } + + public static Vimx FromPath(string path) + => BFastHelpers.Read(path, b => new Vimx(b)); + + public BFast ToBFast() + { + var bfast = new BFast(); + bfast.SetArray(BufferNames.Meta, MetaHeader.Default.ToBytes()); + bfast.SetArray(BufferNames.Header, Header.ToVimxBytes()); + bfast.SetBFast(BufferNames.Scene, Scene.ToBFast(), BufferCompression.Scene); + bfast.SetBFast(BufferNames.Materials, Materials.ToBFast(), BufferCompression.Materials); + + for(var i =0; i < Chunks.Length; i++) + { + bfast.SetBFast(BufferNames.Chunk(i), Chunks[i].ToBFast(), BufferCompression.Chunks); + } + + return bfast; + } + } +} diff --git a/src/cs/vim/Vim.Format.Vimx/VimxHeader.cs b/src/cs/vim/Vim.Format.Vimx/VimxHeader.cs new file mode 100644 index 00000000..a513ae82 --- /dev/null +++ b/src/cs/vim/Vim.Format.Vimx/VimxHeader.cs @@ -0,0 +1,35 @@ +using System.Text; +using Vim.Util; + +namespace Vim.Format.VimxLib +{ + public static class VimxHeader + { + static SerializableVersion CurrentVersion = SerializableVersion.Parse("0.1.0"); + public static SerializableHeader FromString(string header) + { + return SerializableHeader.FromString(header.Replace("vimx", "vim")); + } + public static SerializableHeader FromBytes(byte[] header) + { + return FromString(Encoding.UTF8.GetString(header)); + } + + public static string ToVimxString(this SerializableHeader header) + { + return header.ToString().Replace("vim", "vimx"); + } + + public static byte[] ToVimxBytes(this SerializableHeader header) + { + return header.ToVimxString().ToBytesUtf8(); + } + + public static SerializableHeader CreateDefault() + { + return new SerializableHeader( + "Vim.Vimx.Converter", new SerializableVersion(), CurrentVersion.ToString() + ); + } + } +} diff --git a/src/cs/vim/Vim.Format/Interfaces.cs b/src/cs/vim/Vim.Format/Interfaces.cs new file mode 100644 index 00000000..0fc95986 --- /dev/null +++ b/src/cs/vim/Vim.Format/Interfaces.cs @@ -0,0 +1,128 @@ +using System.IO; +using Vim.Format.ObjectModel; +using Vim.G3d; +using Vim.Math3d; +using Vim.Util; + +namespace Vim.Format +{ + public interface IVimModel + { + /// + /// The schema version contained in the header of the VIM file. + /// + SerializableVersion SchemaVersion { get; } + + /// + /// The header of the VIM file. + /// + SerializableHeader Header { get; } + + /// + /// The serializable version of the VIM model. + /// + SerializableDocument SerializableDocument { get; } + + /// + /// The VIM file path if it was loaded from a file on disk. + /// + string FilePath { get; } // not null if the VIM was loaded from a file on disk + + /// + /// The string buffer for the Entities + /// + string[] StringBuffer { get; } + + /// + /// The entities contained in the VIM. + /// + EntityTableSet Entities { get; } + + /// + /// The collection of all instances in the VIM (i.e. the renderable geometry) + /// + IVimInstance[] Instances { get; } + + /// + /// The world-space bounding box surrounding all the instances in the model. + /// + AABox BoundingBox { get; } + + /// + /// Merges this VIM model with the other VIM model and returns a new one. + /// + IVimModel Merge(IVimModel other); + + /// + /// Writes the VIM model to the given stream + /// + void Write(Stream stream); + + /// + /// Writes the VIM model to the given file path. + /// Deletes any existing file prior to writing. + /// Creates the parent directory if it does not already exist. + /// + void Write(string filePath); + } + + public interface IVimInstance + { + /// + /// The index of the instance. + /// + int Index { get; } + + /// + /// The instance flags associated to this instance. + /// + InstanceFlags InstanceFlags { get; } + + /// + /// The world transform of the instance. + /// + Matrix4x4 WorldTransform { get; } + + /// + /// The mesh associated to an instance can be null. + /// + IVimMesh Mesh { get; } + + /// + /// The Node entity related to this instance. + /// There is a 1:1 relationship between instances and nodes. + /// + Node Node { get; } + + /// + /// The world-space axis aligned bounding box of the instance. + /// + AABox BoundingBox { get; } + } + + public interface IVimMesh + { + int Index { get; } + IVimSubmesh[] Submeshes { get; } + void Validate(); + IVimMesh Transform(Matrix4x4 mat); + } + + public interface IVimSubmesh + { + int Index { get; } + IVimRenderMaterial Material { get; } + Vector3[] Vertices { get; } + int[] Indices { get; } + void Validate(); + IVimSubmesh Transform(Matrix4x4 mat); + } + + public interface IVimRenderMaterial + { + int Index { get; } + Vector4 Color { get; } // rgba + float Glossiness { get; } + float Smoothness { get; } + } +} diff --git a/src/cs/vim/Vim.Format/Merge/MergeConfigFiles.cs b/src/cs/vim/Vim.Format/Merge/MergeConfigFiles.cs index 5ec67293..80cc7c9a 100644 --- a/src/cs/vim/Vim.Format/Merge/MergeConfigFiles.cs +++ b/src/cs/vim/Vim.Format/Merge/MergeConfigFiles.cs @@ -13,18 +13,6 @@ public class MergeConfigFiles /// public (string VimFilePath, Matrix4x4 Transform)[] InputVimFilePathsAndTransforms { get; } - /// - /// The input VIM file paths - /// - public string[] InputVimFilePaths - => InputVimFilePathsAndTransforms.Select(t => t.VimFilePath).ToArray(); - - /// - /// The input VIM file path transforms - /// - public Matrix4x4[] InputTransforms - => InputVimFilePathsAndTransforms.Select(t => t.Transform).ToArray(); - /// /// The merged VIM file path. /// diff --git a/src/cs/vim/Vim.Format/Merge/MergeConfigVimScenes.cs b/src/cs/vim/Vim.Format/Merge/MergeConfigVimScenes.cs index 2baebd57..713d4897 100644 --- a/src/cs/vim/Vim.Format/Merge/MergeConfigVimScenes.cs +++ b/src/cs/vim/Vim.Format/Merge/MergeConfigVimScenes.cs @@ -8,7 +8,7 @@ public class MergeConfigVimScenes /// /// The input VIM scenes and their transforms. /// - public (VimScene VimScene, Matrix4x4 Transform)[] InputVimScenesAndTransforms { get; } + private (VimScene VimScene, Matrix4x4 Transform)[] InputVimScenesAndTransforms { get; } /// /// The input VIM scenes diff --git a/src/cs/vim/Vim.Format/Merge/MergeService.cs b/src/cs/vim/Vim.Format/Merge/MergeService.cs index ec44d4f7..81f04c40 100644 --- a/src/cs/vim/Vim.Format/Merge/MergeService.cs +++ b/src/cs/vim/Vim.Format/Merge/MergeService.cs @@ -4,12 +4,10 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Vim.BFast; +using Vim.BFastLib; using Vim.Format.Geometry; -using Vim.LinqArray; using Vim.Math3d; using Vim.Format.ObjectModel; - using Vim.Util; namespace Vim.Format.Merge @@ -120,7 +118,7 @@ public static DocumentBuilder MergeVimScenes( for (var i = 0; i < materialTable.NumRows; ++i) { - db.Materials.Add(ObjectModelStore.ConvertMaterialEntityFieldsToRenderableMaterial( + db.Geometry.AddMaterial(ObjectModelStore.ConvertMaterialEntityFieldsToRenderableMaterial( colorX: (float)colorXColumn.ElementAtOrDefault(i), colorY: (float)colorYColumn.ElementAtOrDefault(i), colorZ: (float)colorZColumn.ElementAtOrDefault(i), @@ -135,18 +133,18 @@ public static DocumentBuilder MergeVimScenes( progress?.Report("Merging geometry"); ct.ThrowIfCancellationRequested(); - var materialCounts = vims.Select(v => v.Materials.Count); - var materialOffsets = materialCounts.ToIArray().PartialSums().DropLast(); + var materialCounts = vims.Select(v => v.Materials.Length).ToArray(); + var materialOffsets = materialCounts.PostAccumulate((x, y) => x + y).DropLast(); - db.Meshes.AddRange(vims - .SelectMany((vim, vimIndex) => vim.Meshes.Select(mesh => (mesh, vimIndex)).ToEnumerable()) + db.Geometry.AddMeshes(vims + .SelectMany((vim, vimIndex) => vim.Meshes.Select(mesh => (mesh, vimIndex))) .Select( - pair => new DocumentBuilder.SubdividedMesh( - indices: pair.mesh.Indices?.ToList(), - vertices: pair.mesh.Vertices?.ToList(), - submeshesIndexOffset: pair.mesh.SubmeshIndexOffsets?.ToList(), - submeshMaterials: pair.mesh.SubmeshMaterials.Select( - mat => mat == -1 ? -1 : mat + materialOffsets[pair.vimIndex])?.ToList() + pair => new VimMesh( + indices: pair.mesh.indices, + vertices: pair.mesh.vertices, + submeshIndexOffsets: pair.mesh.submeshIndexOffsets, + submeshMaterials: pair.mesh.submeshMaterials.Select( + mat => mat == -1 ? -1 : mat + materialOffsets[pair.vimIndex]).ToArray() ) ) .ToList()); @@ -162,17 +160,17 @@ public static DocumentBuilder MergeVimScenes( vimTransforms = gridTransforms.Zip(vimTransforms, (g, t) => g * t).ToArray(); } - var meshCounts = vims.Select(v => v.Meshes.Count); - var meshOffsets = meshCounts.ToIArray().PartialSums().DropLast(); + var meshCounts = vims.Select(v => v.Meshes.Length).ToArray(); + var meshOffsets = meshCounts.PostAccumulate((x, y) => x + y).DropLast(); // Merge the instances progress?.Report("Merging instances"); ct.ThrowIfCancellationRequested(); var allIdentity = vimTransforms.All(t => t.IsIdentity); - db.Instances.AddRange( + db.Geometry.AddInstances( vims - .SelectMany((vim, vimIndex) => vim.VimNodes.Select(node => (node, vimIndex)).ToEnumerable()) + .SelectMany((vim, vimIndex) => vim.Nodes.Select(node => (node, vimIndex))) .Select(pair => new DocumentBuilder.Instance() { ParentIndex = -1, @@ -186,7 +184,7 @@ public static DocumentBuilder MergeVimScenes( // Merge the assets progress?.Report("Merging assets"); ct.ThrowIfCancellationRequested(); - foreach (var asset in vims.SelectMany(vim => vim.Document.Assets.Values.ToEnumerable())) + foreach (var asset in vims.SelectMany(vim => vim.Document.Assets.Values)) db.AddAsset(asset); return db; @@ -195,7 +193,7 @@ public static DocumentBuilder MergeVimScenes( /// /// Throws if the given VIM files do not all have the same object model schema major version. /// - public static void ValidateSameObjectModelSchemaMajorVersion(VimScene[] vims) + private static void ValidateSameObjectModelSchemaMajorVersion(VimScene[] vims) { var objectModelMajorVersions = vims .Select(v => v.Document.Header.Schema.Major) @@ -222,10 +220,10 @@ public static void ValidateSameObjectModelSchemaMajorVersion(VimScene[] vims) /// /// Returns a collection of transforms based on the largest dimension of the largest VIM bounding box. /// - public static Matrix4x4[] GetGridTransforms(VimScene[] vims, float padding) + private static Matrix4x4[] GetGridTransforms(VimScene[] vims, float padding) { var boxes = vims.Select(v => v.BoundingBox()).ToArray(); - var centerBottomTransforms = boxes.Select(b => Matrix4x4.CreateTranslation(-b.CenterBottom)).ToIArray(); + var centerBottomTransforms = boxes.Select(b => Matrix4x4.CreateTranslation(-b.CenterBottom)).ToArray(); var columnSize = boxes.Select(b => b.Extent.X).Max() + padding; var rowSize = boxes.Select(b => b.Extent.Y).Max() + padding; @@ -243,7 +241,7 @@ public static Matrix4x4[] GetGridTransforms(VimScene[] vims, float padding) return transforms; } - private static IArray GetGridOfTransforms(int count, int numRows, float xSide, float ySide) + private static IList GetGridOfTransforms(int count, int numRows, float xSide, float ySide) => count.Select(i => Matrix4x4.CreateTranslation(i % numRows * xSide, i / numRows * ySide, 0)); private static void MergeEntities( @@ -261,7 +259,7 @@ private static void MergeEntities( { cancellationToken.ThrowIfCancellationRequested(); - foreach (var entityTable in vim.Document.EntityTables.Values.ToEnumerable()) + foreach (var entityTable in vim.Document.EntityTables.Values) { cancellationToken.ThrowIfCancellationRequested(); @@ -299,7 +297,7 @@ private static Dictionary ComputeMergedEntityTableOffsets(IEnu var entityTableOffsetMap = new Dictionary(); foreach (var doc in documents) { - foreach (var entityTable in doc.EntityTables.Values.ToEnumerable()) + foreach (var entityTable in doc.EntityTables.Values) { var entityTableName = entityTable.Name; diff --git a/src/cs/vim/Vim.Format/Merge/MergeService2.cs b/src/cs/vim/Vim.Format/Merge/MergeService2.cs new file mode 100644 index 00000000..58220923 --- /dev/null +++ b/src/cs/vim/Vim.Format/Merge/MergeService2.cs @@ -0,0 +1,285 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Vim.BFastLib; +using Vim.Math3d; +using Vim.Format.ObjectModel; +using Vim.Util; + +namespace Vim.Format.Merge +{ + public static class MergeService2 + { + public static IVimModel MergeVimModels( + IVimModel[] vims, + MergeConfigOptions optionsConfig = null, + IProgress progress = null, + CancellationToken ct = default) + { + optionsConfig = optionsConfig ?? new MergeConfigOptions(); + + // Validate that all VIMs are in the same object model major version + ValidateSameObjectModelSchemaMajorVersion(vims); + + var db = new DocumentBuilder(optionsConfig.GeneratorString, SchemaVersion.Current, optionsConfig.VersionString); + + // Merge the entity data + progress?.Report("Merging entities"); + ct.ThrowIfCancellationRequested(); + MergeEntities(db, vims.Select(v => v.Entities).ToArray(), optionsConfig.KeepBimData, ct); + + // Optionally deduplicate the entity data. + if (optionsConfig.DeduplicateEntities) + { + progress?.Report("Deduplicating entities"); + ct.ThrowIfCancellationRequested(); + RemappedEntityTableBuilder.DeduplicateEntities(db, ct); + } + + // Merge the materials + // + // IMPORTANT: there must be a 1:1 aligned relationship between the material entities and the renderable materials. + // To ensure this constraint, We use the existing material entities in the document builder, whose entities have been + // previously populated and optionally deduplicated above. + progress?.Report("Merging materials"); + ct.ThrowIfCancellationRequested(); + + var materialTable = db.Tables.Values.FirstOrDefault(et => et.Name == TableNames.Material); + if (materialTable != null) + { + var mdcs = materialTable.DataColumns; + + var colorXColumn = mdcs.TryGetValue("double:Color.X", out var cX) + ? cX.AsArray() + : Array.Empty(); + + var colorYColumn = mdcs.TryGetValue("double:Color.Y", out var cY) + ? cY.AsArray() + : Array.Empty(); + + var colorZColumn = mdcs.TryGetValue("double:Color.Z", out var cZ) + ? cZ.AsArray() + : Array.Empty(); + + var transparencyColumn = mdcs.TryGetValue("double:Transparency", out var t) + ? t.AsArray() + : Array.Empty(); + + var glossinessColumn = mdcs.TryGetValue("double:Glossiness", out var g) + ? g.AsArray() + : Array.Empty(); + + var smoothnessColumn = mdcs.TryGetValue("double:Smoothness", out var s) + ? s.AsArray() + : Array.Empty(); + + for (var i = 0; i < materialTable.NumRows; ++i) + { + db.Geometry.AddMaterial(ObjectModelStore.ConvertMaterialEntityFieldsToRenderableMaterial( + colorX: (float)colorXColumn.ElementAtOrDefault(i), + colorY: (float)colorYColumn.ElementAtOrDefault(i), + colorZ: (float)colorZColumn.ElementAtOrDefault(i), + transparency: (float)transparencyColumn.ElementAtOrDefault(i), + glossiness: (float)glossinessColumn.ElementAtOrDefault(i), + smoothness: (float)smoothnessColumn.ElementAtOrDefault(i) + )); + } + } + + // Merge the geometry + progress?.Report("Merging geometry"); + ct.ThrowIfCancellationRequested(); + + var materialCounts = vims.SelectMany(v => v.Instances.Select(i => i.Mesh.Submeshes.Max(sm => sm.Material.Index))).ToArray(); + var materialOffsets = materialCounts.PostAccumulate((x, y) => x + y).DropLast(); + + var meshes = vims + .SelectMany((vim, vimIndex) => vim.Instances.Select(i => (i.Mesh, vimIndex))) + .Select( + pair => new VimMesh2( + pair.Mesh.Index, + pair.Mesh.Submeshes.Select( + sm => (IVimSubmesh) new VimSubmesh2( + sm.Index, + new VimRenderMaterial( + sm.Material.Index == -1 + ? -1 + : sm.Material.Index + + materialOffsets[pair.vimIndex], + sm.Material.Color, + sm.Material.Glossiness, + sm.Material.Smoothness + ), + sm.Vertices, + sm.Indices + ) + ).ToArray())).ToArray(); + + // TODO: implement AddMeshes for IVimMesh[] + // db.Geometry.AddMeshes(meshes); + + ct.ThrowIfCancellationRequested(); + + // Apply the optional grid transforms. + var vimTransforms = new Matrix4x4[vims.Length]; + if (optionsConfig.MergeAsGrid) + { + progress?.Report("Calculating merge grid"); + vimTransforms = GetGridTransforms(vims, optionsConfig.GridPadding); + } + + var meshCounts = vims.Select(v => v.Instances.Length).ToArray(); + var meshOffsets = meshCounts.PostAccumulate((x, y) => x + y).DropLast(); + + // Merge the instances + progress?.Report("Merging instances"); + ct.ThrowIfCancellationRequested(); + + var allIdentity = vimTransforms.All(t => t.IsIdentity); + var instances = vims + .SelectMany((vim, vimIndex) => vim.Instances.Select(instance => (instance, vimIndex))) + .Select(pair => new DocumentBuilder.Instance + { + ParentIndex = -1, + InstanceFlags = pair.instance.InstanceFlags, + MeshIndex = pair.instance.Mesh == null ? -1 : pair.instance.Mesh.Index + meshOffsets[pair.vimIndex], + Transform = allIdentity + ? pair.instance.WorldTransform + : pair.instance.WorldTransform * vimTransforms[pair.vimIndex] + }).ToArray(); + + db.Geometry.AddInstances(instances); + + // Merge the assets + progress?.Report("Merging assets"); + ct.ThrowIfCancellationRequested(); + foreach (var asset in vims.SelectMany(vim => vim.SerializableDocument.Assets)) + db.AddAsset(asset); + + // TODO: implement conversion from DocumentBuilder to IVimModel (or use an alternative approach) + return null; + } + + /// + /// Throws if the given VIM files do not all have the same object model schema major version. + /// + private static void ValidateSameObjectModelSchemaMajorVersion(IVimModel[] vims) + { + var objectModelMajorVersions = vims + .Select(v => v.SerializableDocument.Header.Schema.Major) + .Distinct() + .OrderBy(i => i) + .ToArray(); + + // If we are only dealing with one major object model schema version, we are fine. + if (objectModelMajorVersions.Length == 1) + return; + + // Throw otherwise with a helpful error message. + var sb = new StringBuilder(); + sb.AppendLine($"Object model schema major version mismatch ({string.Join(", ", objectModelMajorVersions.Select(v => $"v{v}.*"))})"); + sb.AppendLine(); + foreach (var vim in vims.OrderBy(v => v.SerializableDocument.Header.Schema.Major)) + sb.AppendLine($"- v{vim.SerializableDocument.Header.Schema}: '{vim.FilePath}'"); + sb.AppendLine(); + sb.AppendLine("Please ensure the VIM files have all been exported with matching schema major versions."); + + throw new HResultException((int)ErrorCode.VimMergeObjectModelMajorVersionMismatch, sb.ToString()); + } + + /// + /// Returns a collection of transforms based on the largest dimension of the largest VIM bounding box. + /// + private static Matrix4x4[] GetGridTransforms(IVimModel[] vims, float padding) + { + var boxes = vims.Select(v => v.BoundingBox).ToArray(); + var centerBottomTransforms = boxes.Select(b => Matrix4x4.CreateTranslation(-b.CenterBottom)).ToArray(); + + var columnSize = boxes.Select(b => b.Extent.X).Max() + padding; + var rowSize = boxes.Select(b => b.Extent.Y).Max() + padding; + + var numRows = (int)Math.Sqrt(vims.Length).Ceiling(); + + var transforms = GetGridOfTransforms( + vims.Length, + numRows, + columnSize, + rowSize) + .Zip(centerBottomTransforms, (g, b) => b * g) + .ToArray(); + + return transforms; + } + + private static IList GetGridOfTransforms(int count, int numRows, float xSide, float ySide) + => count.Select(i => Matrix4x4.CreateTranslation(i % numRows * xSide, i / numRows * ySide, 0)); + + private static void MergeEntities(DocumentBuilder db, EntityTableSet[] entities, bool keepBimData, CancellationToken cancellationToken) + { + // Compute the offsets for the new entities + var offsets = ComputeMergedEntityTableOffsets(entities); + + // Collect the entity tables, grouped by name + var mergedTableBuilders = new Dictionary(); + foreach (var set in entities) + { + cancellationToken.ThrowIfCancellationRequested(); + + foreach (var entityTable in set.RawTableMap.Values) + { + cancellationToken.ThrowIfCancellationRequested(); + + var name = entityTable.Name; + if (VimConstants.ComputedTableNames.Contains(entityTable.Name)) + continue; + + if (!keepBimData && !VimConstants.NonBimNames.Contains(entityTable.Name)) + continue; + + var mergedTableBuilder = mergedTableBuilders.GetOrCompute(name, (s) => new MergedTableBuilder(s)); + // TODO: implement AddTableSet for EntityTableSet + // mergedTableBuilder.AddTable(entityTable, offsets); + } + } + + // Add the new merged table builder + var tableBuilders = mergedTableBuilders.Values.Select(mtb => db.GetTableBuilderOrCreate(mtb.Name)).ToArray(); + Parallel.For(0, tableBuilders.Length, i => + { + cancellationToken.ThrowIfCancellationRequested(); + + var tb = tableBuilders[i]; + var mtb = mergedTableBuilders[tb.Name]; + mtb.UpdateTableBuilder(tb, cancellationToken); + }); + } + + /// + /// For every entity table in each document, computes the offset of that entity table + /// in a merged document. This is used for remapping entity table relations when merging VIM files. + /// + private static Dictionary ComputeMergedEntityTableOffsets(EntityTableSet[] entities) + { + var aggregateOffsetMap = new Dictionary(); + var entityTableOffsetMap = new Dictionary(); + foreach (var set in entities) + { + foreach ((var name, var table) in set.RawTableMap) + { + // Add the entity table name to the aggregate offset map + aggregateOffsetMap.TryAdd(name, 0); + + // Assign the current offset to the given entity table. + entityTableOffsetMap.Add(name, aggregateOffsetMap[name]); + + // Update the aggregated offsets + aggregateOffsetMap[name] += table.AllColumns.First().Data.Length; + } + } + return entityTableOffsetMap; + } + } +} diff --git a/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs b/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs index 3237114e..afae57d4 100644 --- a/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs +++ b/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs @@ -2,40 +2,38 @@ using System.Diagnostics; using System.Linq; using System.Threading; -using Vim.BFast; -using Vim.LinqArray; +using Vim.BFastLib; using Vim.Util; namespace Vim.Format.Merge { - public class MergedTableBuilder + internal class MergedTableBuilder { public readonly string Name; - public int NumRows; + private int _numRows; public MergedTableBuilder(string name) => Name = name; - public Dictionary DataColumns = new Dictionary(); - public DictionaryOfLists IndexColumns = new DictionaryOfLists(); - public DictionaryOfLists StringColumns = new DictionaryOfLists(); + private readonly Dictionary _dataColumns = new Dictionary(); + private readonly DictionaryOfLists _indexColumns = new DictionaryOfLists(); + private readonly DictionaryOfLists _stringColumns = new DictionaryOfLists(); public void AddTable(EntityTable entityTable, Dictionary entityIndexOffsets) { - Debug.Assert(entityIndexOffsets[entityTable] == NumRows); + Debug.Assert(entityIndexOffsets[entityTable] == _numRows); // Add index columns from the entity table - foreach (var k in entityTable.IndexColumns.Keys.ToEnumerable()) + foreach (var col in entityTable.IndexColumns) { - var col = entityTable.IndexColumns[k]; var indexColumnFullName = col.Name; - if (!IndexColumns.ContainsKey(indexColumnFullName)) - IndexColumns.Add(indexColumnFullName, Enumerable.Repeat(-1, NumRows).ToList()); + if (!_indexColumns.ContainsKey(indexColumnFullName)) + _indexColumns.Add(indexColumnFullName, Enumerable.Repeat(-1, _numRows).ToList()); Debug.Assert(col.Array.Length == entityTable.NumRows); var relatedTable = col.GetRelatedTable(entityTable.Document); - var vals = IndexColumns[indexColumnFullName]; + var vals = _indexColumns[indexColumnFullName]; var offset = entityIndexOffsets[relatedTable]; foreach (var v in col.Array) @@ -43,83 +41,81 @@ public void AddTable(EntityTable entityTable, Dictionary entit } // Add data columns from the entity table - foreach (var colName in entityTable.DataColumns.Keys.ToEnumerable()) + foreach (var col in entityTable.DataColumns) { - var col = entityTable.DataColumns[colName]; - if (!DataColumns.ContainsKey(colName)) + if (!_dataColumns.ContainsKey(col.Name)) { - DataColumns[colName] = col; + _dataColumns[col.Name] = col; } else { - var cur = DataColumns[colName]; - DataColumns[colName] = cur.ConcatDataColumnBuffers(col, colName.GetTypePrefix()); + var cur = _dataColumns[col.Name]; + _dataColumns[col.Name] = cur.ConcatDataColumnBuffers(col, SerializableEntityTable.GetTypeFromName(col.Name)); } } // Add string columns from the entity table - foreach (var k in entityTable.StringColumns.Keys.ToEnumerable()) + foreach (var col in entityTable.StringColumns) { - if (!StringColumns.ContainsKey(k)) - StringColumns.Add(k, Enumerable.Repeat("", NumRows).ToList()); + if (!_stringColumns.ContainsKey(col.Name)) + _stringColumns.Add(col.Name, Enumerable.Repeat("", _numRows).ToList()); - var col = entityTable.StringColumns[k]; Debug.Assert(col.Array.Length == entityTable.NumRows); - var vals = StringColumns[k]; + var vals = _stringColumns[col.Name]; foreach (var v in col.Array) vals.Add(entityTable.Document.GetString(v)); } // For each column in the builder but not in the entity table add default values - foreach (var kv in DataColumns) + foreach (var kv in _dataColumns) { var colName = kv.Key; - var typePrefix = colName.GetTypePrefix(); - if (!entityTable.DataColumns.Contains(colName)) + var typePrefix = SerializableEntityTable.GetTypeFromName(colName); + if (entityTable.DataColumns.All(c => c.Name != colName)) { - var cur = DataColumns[colName]; + var cur = _dataColumns[colName]; var defaultBuffer = ColumnExtensions.CreateDefaultDataColumnBuffer(entityTable.NumRows, typePrefix); - DataColumns[colName] = cur.ConcatDataColumnBuffers(defaultBuffer, typePrefix); + _dataColumns[colName] = cur.ConcatDataColumnBuffers(defaultBuffer, typePrefix); } } - foreach (var kv in IndexColumns) + foreach (var kv in _indexColumns) { - if (!entityTable.IndexColumns.Contains(kv.Key)) - IndexColumns[kv.Key].AddRange(Enumerable.Repeat(-1, entityTable.NumRows)); + if (entityTable.IndexColumns.All(c => c.Name != kv.Key)) + _indexColumns[kv.Key].AddRange(Enumerable.Repeat(-1, entityTable.NumRows)); } - foreach (var kv in StringColumns) + foreach (var kv in _stringColumns) { - if (!entityTable.StringColumns.Contains(kv.Key)) - StringColumns[kv.Key].AddRange(Enumerable.Repeat("", entityTable.NumRows)); + if (entityTable.StringColumns.All(c => c.Name != kv.Key)) + _stringColumns[kv.Key].AddRange(Enumerable.Repeat("", entityTable.NumRows)); } - NumRows += entityTable.NumRows; + _numRows += entityTable.NumRows; - foreach (var kv in DataColumns) - Debug.Assert(kv.Value.Data.Length == NumRows); - foreach (var kv in IndexColumns) - Debug.Assert(kv.Value.Count == NumRows); - foreach (var kv in StringColumns) - Debug.Assert(kv.Value.Count == NumRows); + foreach (var kv in _dataColumns) + Debug.Assert(kv.Value.Data.Length == _numRows); + foreach (var kv in _indexColumns) + Debug.Assert(kv.Value.Count == _numRows); + foreach (var kv in _stringColumns) + Debug.Assert(kv.Value.Count == _numRows); } public void UpdateTableBuilder(EntityTableBuilder tb, CancellationToken cancellationToken = default) { - foreach (var kv in DataColumns) + foreach (var kv in _dataColumns) { cancellationToken.ThrowIfCancellationRequested(); tb.AddDataColumn(kv.Key, kv.Value); } - foreach (var kv in StringColumns) + foreach (var kv in _stringColumns) { cancellationToken.ThrowIfCancellationRequested(); tb.AddStringColumn(kv.Key, kv.Value.ToArray()); } - foreach (var kv in IndexColumns) + foreach (var kv in _indexColumns) { cancellationToken.ThrowIfCancellationRequested(); tb.AddIndexColumn(kv.Key, kv.Value.ToArray()); diff --git a/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs b/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs index 231bb52a..48e98ae6 100644 --- a/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs +++ b/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs @@ -6,7 +6,7 @@ namespace Vim.Format.Merge { - public class RemappedEntityTableBuilder + internal class RemappedEntityTableBuilder { public EntityTableBuilder EntityTableBuilder { get; } public int[] OldToNewIndexMap { get; } @@ -96,7 +96,7 @@ private static RemappedEntityTableBuilder CreateRemapped(EntityTableBuilder e { var colName = kv.Key; var col = kv.Value; - var typePrefix = colName.GetTypePrefix(); + var typePrefix = SerializableEntityTable.GetTypeFromName(colName); var newCol = col.CopyDataColumn(typePrefix, retainedIndices); remapped.AddDataColumn(colName, newCol); } @@ -137,7 +137,7 @@ private static void UpdateEntityTableBuilderRelations( var indexColumn = kv.Value; // Get the related index remapping. - var tableName = DocumentExtensions.GetRelatedTableNameFromColumnName(indexColumnName); + var tableName = TableNameExtensions.GetRelatedTableNameFromIndexColumnName(indexColumnName); if (!remappedTableIndices.TryGetValue(tableName, out var oldToNewIndexMap)) continue; diff --git a/src/cs/vim/Vim.Format/ObjectModel/ElementIndexMaps.cs b/src/cs/vim/Vim.Format/ObjectModel/ElementIndexMaps.cs index 4ad3c786..d6fea0ca 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/ElementIndexMaps.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/ElementIndexMaps.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Vim.Util; using IndexMap = System.Collections.Generic.Dictionary; @@ -85,12 +86,12 @@ public ElementIndexMaps(EntityTableSet entityTables, bool inParallel = true) } } - public static readonly string ElementIndexColumnName = ColumnExtensions.GetIndexColumnName(TableNames.Element, nameof(Element)); + private static readonly string ElementIndexColumnName = ColumnExtensions.GetIndexColumnName(TableNames.Element, nameof(Element)); - public static DictionaryOfLists GetElementIndicesMap(EntityTable et) + private static DictionaryOfLists GetElementIndicesMap(EntityTable et) { var indicesMap = new DictionaryOfLists(); - var elementIndices = et?.IndexColumns[ElementIndexColumnName]?.GetTypedData(); + var elementIndices = et?.IndexColumns.FirstOrDefault(col => col.Name == ElementIndexColumnName)?.GetTypedData(); if (elementIndices == null) return indicesMap; for (var i = 0; i < elementIndices.Length; ++i) @@ -98,7 +99,7 @@ public static DictionaryOfLists GetElementIndicesMap(EntityTable et) return indicesMap; } - public static DictionaryOfLists GetElementIndicesMap(EntityTable_v2 et) + private static DictionaryOfLists GetElementIndicesMap(EntityTable_v2 et) { var indicesMap = new DictionaryOfLists(); var elementIndices = et?.IndexColumns[ElementIndexColumnName]?.GetTypedData(); @@ -109,10 +110,10 @@ public static DictionaryOfLists GetElementIndicesMap(EntityTable_v2 et return indicesMap; } - public static IndexMap GetElementIndexMap(EntityTable et) + private static IndexMap GetElementIndexMap(EntityTable et) { var indexMap = new IndexMap(); - var elementIndices = et?.IndexColumns[ElementIndexColumnName]?.GetTypedData(); + var elementIndices = et?.IndexColumns.FirstOrDefault(col => col.Name == ElementIndexColumnName)?.GetTypedData(); if (elementIndices == null) return indexMap; for (var i = 0; i < elementIndices.Length; ++i) @@ -120,7 +121,7 @@ public static IndexMap GetElementIndexMap(EntityTable et) return indexMap; } - public static IndexMap GetElementIndexMap(EntityTable_v2 et) + private static IndexMap GetElementIndexMap(EntityTable_v2 et) { var indexMap = new IndexMap(); var elementIndices = et?.IndexColumns[ElementIndexColumnName]?.GetTypedData(); diff --git a/src/cs/vim/Vim.Format/ObjectModel/ElementInfo.cs b/src/cs/vim/Vim.Format/ObjectModel/ElementInfo.cs index f8f40bac..cc896bb6 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/ElementInfo.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/ElementInfo.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Vim.LinqArray; +using System.Linq; namespace Vim.Format.ObjectModel { @@ -29,8 +29,8 @@ public int FamilyInstanceIndex } } - private IArray _parameterIndices; - public IArray ParameterIndices + private int[] _parameterIndices; + public int[] ParameterIndices { get { @@ -39,7 +39,7 @@ public IArray ParameterIndices _parameterIndices = (DocumentModel.ElementIndexMaps.ParameterIndicesFromElementIndex .TryGetValue(ElementIndex, out var parameterIndices) ? parameterIndices : new List()) - .ToIArray(); + .ToArray(); return _parameterIndices; } @@ -108,7 +108,7 @@ public bool IsSystem public Family Family => DocumentModel.FamilyList.ElementAtOrDefault(FamilyIndex); public System System => DocumentModel.SystemList.ElementAtOrDefault(SystemIndex); public Element SystemElement => DocumentModel.ElementList.ElementAtOrDefault(SystemElementIndex); - public IEnumerable Parameters => DocumentModel.ParameterList.SelectByIndex(ParameterIndices).ToEnumerable(); + public IEnumerable Parameters => ParameterIndices.Select(i => DocumentModel.ParameterList[i]); [Flags] public enum ParameterScope diff --git a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelExtensions.cs b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelExtensions.cs index ceab0b2c..e45ccb5c 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelExtensions.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelExtensions.cs @@ -4,7 +4,6 @@ using System.IO; using System.Linq; using Vim.Util; -using Vim.LinqArray; namespace Vim.Format.ObjectModel { @@ -22,68 +21,15 @@ public static ElementInfo GetElementInfo(this DocumentModel documentModel, Entit public static string GetUrn(this ElementInfo elementInfo) => Urn.GetElementUrn(Urn.VimNID, elementInfo.Element); - public static string GetUrn(this BimDocument bd) - => Urn.GetBimDocumentUrn(Urn.VimNID, bd); - public static Element CreateSyntheticElement(string name, string type) - => new Element - { - Id = VimConstants.SyntheticElementId, - Name = name, - Type = type, - UniqueId = $"{name}_{type}" // NOTE: we need to assign a UniqueId for merging purposes. - }; - - public static Element CreateParameterHolderElement(string bimDocumentName) - => CreateSyntheticElement(bimDocumentName, VimConstants.BimDocumentParameterHolderElementType); + => new Element { Id = VimConstants.SyntheticElementId, Name = name, Type = type }; public static Element CreateParameterHolderElement(this BimDocument bd) - => CreateParameterHolderElement(bd.Name); - - public static DictionaryOfLists GetAssetsInViewOrderedByViewIndex(this DocumentModel dm) - => dm.AssetInViewList.GroupBy(aiv => aiv.View.Index).ToDictionaryOfLists(); + => CreateSyntheticElement(bd.Name, VimConstants.BimDocumentParameterHolderElementType); public static string GetBimDocumentFileName(this DocumentModel dm, int bimDocumentIndex) => Path.GetFileName(dm.GetBimDocumentPathName(bimDocumentIndex)); - public static IArray GetBimDocumentDisplayUnits(this DocumentModel dm, BimDocument bd) - => dm.DisplayUnitInBimDocumentList - .Where(item => item.BimDocument.Index == bd.Index) - .Select(item => item.DisplayUnit) - .ToIArray(); - - public static IArray GetBimDocumentPhases(this DocumentModel dm, BimDocument bd) - => dm.PhaseOrderInBimDocumentList - .Where(item => item.BimDocument.Index == bd.Index) - .Select(item => item.Phase) - .ToIArray(); - - public const string LengthSpecLegacyPrefix = "UT_Length"; - public const string LengthSpecPrefix = "autodesk.spec.aec:length"; - - public static DisplayUnit GetLengthDisplayUnit(this IArray displayUnits) - => displayUnits.FirstOrDefault(du => - { - var spec = du.Spec; - return spec.StartsWith(LengthSpecPrefix, StringComparison.InvariantCultureIgnoreCase) || - spec.StartsWith(LengthSpecLegacyPrefix, StringComparison.InvariantCultureIgnoreCase); - }); - - public static FamilyType GetFamilyType(this FamilyInstance fi) - => fi?.FamilyType; - - public static string GetFamilyTypeName(this FamilyInstance fi) - => fi?.FamilyType?.Element?.Name ?? ""; - - public static Family GetFamily(this FamilyType ft) - => ft?.Family; - - public static Family GetFamily(this FamilyInstance fi) - => fi?.GetFamilyType()?.GetFamily(); - - public static string GetFamilyName(this FamilyInstance fi) - => fi?.GetFamily()?.Element?.Name ?? ""; - /// /// Extension method using pre-allocated parser for improved performance. /// @@ -160,7 +106,8 @@ public static DataTable GetScheduleAsDataTable(this DocumentModel dm, int schedu var columnSet = new HashSet(columns.Select(c => c.Index)); var cellRecords = dm.ScheduleCellScheduleColumnIndex - .IndicesWhere((colIndex, _) => columnSet.Contains(colIndex)) + .Indices() + .Where((colIndex, _) => columnSet.Contains(colIndex)) .Select(cellIndex => new CellData( dm.GetScheduleCellValue(cellIndex), dm.GetScheduleCellScheduleColumnIndex(cellIndex), diff --git a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs index d45efcb3..a3102b23 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs @@ -5,8 +5,6 @@ using System.Collections.Generic; using System.Linq; using Vim.Math3d; -using Vim.LinqArray; -using Vim.Format.ObjectModel; using Vim.Util; namespace Vim.Format.ObjectModel { @@ -1894,10 +1892,10 @@ public partial class DocumentModel public EntityTable AssetEntityTable { get; } - public IArray AssetBufferName { get; } + public String[] AssetBufferName { get; } public String GetAssetBufferName(int index, String defaultValue = "") => AssetBufferName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; public int NumAsset => AssetEntityTable?.NumRows ?? 0; - public IArray AssetList { get; } + public Asset[] AssetList { get; } public Asset GetAsset(int n) { if (n < 0) return null; @@ -1913,14 +1911,14 @@ public Asset GetAsset(int n) public EntityTable DisplayUnitEntityTable { get; } - public IArray DisplayUnitSpec { get; } + public String[] DisplayUnitSpec { get; } public String GetDisplayUnitSpec(int index, String defaultValue = "") => DisplayUnitSpec?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray DisplayUnitType { get; } + public String[] DisplayUnitType { get; } public String GetDisplayUnitType(int index, String defaultValue = "") => DisplayUnitType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray DisplayUnitLabel { get; } + public String[] DisplayUnitLabel { get; } public String GetDisplayUnitLabel(int index, String defaultValue = "") => DisplayUnitLabel?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; public int NumDisplayUnit => DisplayUnitEntityTable?.NumRows ?? 0; - public IArray DisplayUnitList { get; } + public DisplayUnit[] DisplayUnitList { get; } public DisplayUnit GetDisplayUnit(int n) { if (n < 0) return null; @@ -1938,28 +1936,28 @@ public DisplayUnit GetDisplayUnit(int n) public EntityTable ParameterDescriptorEntityTable { get; } - public IArray ParameterDescriptorName { get; } + public String[] ParameterDescriptorName { get; } public String GetParameterDescriptorName(int index, String defaultValue = "") => ParameterDescriptorName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorGroup { get; } + public String[] ParameterDescriptorGroup { get; } public String GetParameterDescriptorGroup(int index, String defaultValue = "") => ParameterDescriptorGroup?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorParameterType { get; } + public String[] ParameterDescriptorParameterType { get; } public String GetParameterDescriptorParameterType(int index, String defaultValue = "") => ParameterDescriptorParameterType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorIsInstance { get; } + public Boolean[] ParameterDescriptorIsInstance { get; } public Boolean GetParameterDescriptorIsInstance(int index, Boolean defaultValue = default) => ParameterDescriptorIsInstance?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorIsShared { get; } + public Boolean[] ParameterDescriptorIsShared { get; } public Boolean GetParameterDescriptorIsShared(int index, Boolean defaultValue = default) => ParameterDescriptorIsShared?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorIsReadOnly { get; } + public Boolean[] ParameterDescriptorIsReadOnly { get; } public Boolean GetParameterDescriptorIsReadOnly(int index, Boolean defaultValue = default) => ParameterDescriptorIsReadOnly?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorFlags { get; } + public Int32[] ParameterDescriptorFlags { get; } public Int32 GetParameterDescriptorFlags(int index, Int32 defaultValue = default) => ParameterDescriptorFlags?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorGuid { get; } + public String[] ParameterDescriptorGuid { get; } public String GetParameterDescriptorGuid(int index, String defaultValue = "") => ParameterDescriptorGuid?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorStorageType { get; } + public Int32[] ParameterDescriptorStorageType { get; } public Int32 GetParameterDescriptorStorageType(int index, Int32 defaultValue = default) => ParameterDescriptorStorageType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorDisplayUnitIndex { get; } + public int[] ParameterDescriptorDisplayUnitIndex { get; } public int GetParameterDescriptorDisplayUnitIndex(int index) => ParameterDescriptorDisplayUnitIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumParameterDescriptor => ParameterDescriptorEntityTable?.NumRows ?? 0; - public IArray ParameterDescriptorList { get; } + public ParameterDescriptor[] ParameterDescriptorList { get; } public ParameterDescriptor GetParameterDescriptor(int n) { if (n < 0) return null; @@ -1984,14 +1982,14 @@ public ParameterDescriptor GetParameterDescriptor(int n) public EntityTable ParameterEntityTable { get; } - public IArray ParameterValue { get; } + public String[] ParameterValue { get; } public String GetParameterValue(int index, String defaultValue = "") => ParameterValue?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterParameterDescriptorIndex { get; } + public int[] ParameterParameterDescriptorIndex { get; } public int GetParameterParameterDescriptorIndex(int index) => ParameterParameterDescriptorIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ParameterElementIndex { get; } + public int[] ParameterElementIndex { get; } public int GetParameterElementIndex(int index) => ParameterElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumParameter => ParameterEntityTable?.NumRows ?? 0; - public IArray ParameterList { get; } + public Parameter[] ParameterList { get; } public Parameter GetParameter(int n) { if (n < 0) return null; @@ -2009,48 +2007,48 @@ public Parameter GetParameter(int n) public EntityTable ElementEntityTable { get; } - public IArray ElementId { get; } + public Int64[] ElementId { get; } public Int64 GetElementId(int index, Int64 defaultValue = default) => ElementId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementType { get; } + public String[] ElementType { get; } public String GetElementType(int index, String defaultValue = "") => ElementType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementName { get; } + public String[] ElementName { get; } public String GetElementName(int index, String defaultValue = "") => ElementName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementUniqueId { get; } + public String[] ElementUniqueId { get; } public String GetElementUniqueId(int index, String defaultValue = "") => ElementUniqueId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementLocation_X { get; } + public Single[] ElementLocation_X { get; } public Single GetElementLocation_X(int index, Single defaultValue = default) => ElementLocation_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementLocation_Y { get; } + public Single[] ElementLocation_Y { get; } public Single GetElementLocation_Y(int index, Single defaultValue = default) => ElementLocation_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementLocation_Z { get; } + public Single[] ElementLocation_Z { get; } public Single GetElementLocation_Z(int index, Single defaultValue = default) => ElementLocation_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementFamilyName { get; } + public String[] ElementFamilyName { get; } public String GetElementFamilyName(int index, String defaultValue = "") => ElementFamilyName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementIsPinned { get; } + public Boolean[] ElementIsPinned { get; } public Boolean GetElementIsPinned(int index, Boolean defaultValue = default) => ElementIsPinned?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementLevelIndex { get; } + public int[] ElementLevelIndex { get; } public int GetElementLevelIndex(int index) => ElementLevelIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementPhaseCreatedIndex { get; } + public int[] ElementPhaseCreatedIndex { get; } public int GetElementPhaseCreatedIndex(int index) => ElementPhaseCreatedIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementPhaseDemolishedIndex { get; } + public int[] ElementPhaseDemolishedIndex { get; } public int GetElementPhaseDemolishedIndex(int index) => ElementPhaseDemolishedIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementCategoryIndex { get; } + public int[] ElementCategoryIndex { get; } public int GetElementCategoryIndex(int index) => ElementCategoryIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementWorksetIndex { get; } + public int[] ElementWorksetIndex { get; } public int GetElementWorksetIndex(int index) => ElementWorksetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementDesignOptionIndex { get; } + public int[] ElementDesignOptionIndex { get; } public int GetElementDesignOptionIndex(int index) => ElementDesignOptionIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementOwnerViewIndex { get; } + public int[] ElementOwnerViewIndex { get; } public int GetElementOwnerViewIndex(int index) => ElementOwnerViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementGroupIndex { get; } + public int[] ElementGroupIndex { get; } public int GetElementGroupIndex(int index) => ElementGroupIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementAssemblyInstanceIndex { get; } + public int[] ElementAssemblyInstanceIndex { get; } public int GetElementAssemblyInstanceIndex(int index) => ElementAssemblyInstanceIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementBimDocumentIndex { get; } + public int[] ElementBimDocumentIndex { get; } public int GetElementBimDocumentIndex(int index) => ElementBimDocumentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementRoomIndex { get; } + public int[] ElementRoomIndex { get; } public int GetElementRoomIndex(int index) => ElementRoomIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumElement => ElementEntityTable?.NumRows ?? 0; - public IArray ElementList { get; } + public Element[] ElementList { get; } public Element GetElement(int n) { if (n < 0) return null; @@ -2085,24 +2083,24 @@ public Element GetElement(int n) public EntityTable WorksetEntityTable { get; } - public IArray WorksetId { get; } + public Int32[] WorksetId { get; } public Int32 GetWorksetId(int index, Int32 defaultValue = default) => WorksetId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WorksetName { get; } + public String[] WorksetName { get; } public String GetWorksetName(int index, String defaultValue = "") => WorksetName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WorksetKind { get; } + public String[] WorksetKind { get; } public String GetWorksetKind(int index, String defaultValue = "") => WorksetKind?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WorksetIsOpen { get; } + public Boolean[] WorksetIsOpen { get; } public Boolean GetWorksetIsOpen(int index, Boolean defaultValue = default) => WorksetIsOpen?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WorksetIsEditable { get; } + public Boolean[] WorksetIsEditable { get; } public Boolean GetWorksetIsEditable(int index, Boolean defaultValue = default) => WorksetIsEditable?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WorksetOwner { get; } + public String[] WorksetOwner { get; } public String GetWorksetOwner(int index, String defaultValue = "") => WorksetOwner?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WorksetUniqueId { get; } + public String[] WorksetUniqueId { get; } public String GetWorksetUniqueId(int index, String defaultValue = "") => WorksetUniqueId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WorksetBimDocumentIndex { get; } + public int[] WorksetBimDocumentIndex { get; } public int GetWorksetBimDocumentIndex(int index) => WorksetBimDocumentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumWorkset => WorksetEntityTable?.NumRows ?? 0; - public IArray WorksetList { get; } + public Workset[] WorksetList { get; } public Workset GetWorkset(int n) { if (n < 0) return null; @@ -2125,18 +2123,18 @@ public Workset GetWorkset(int n) public EntityTable AssemblyInstanceEntityTable { get; } - public IArray AssemblyInstanceAssemblyTypeName { get; } + public String[] AssemblyInstanceAssemblyTypeName { get; } public String GetAssemblyInstanceAssemblyTypeName(int index, String defaultValue = "") => AssemblyInstanceAssemblyTypeName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AssemblyInstancePosition_X { get; } + public Single[] AssemblyInstancePosition_X { get; } public Single GetAssemblyInstancePosition_X(int index, Single defaultValue = default) => AssemblyInstancePosition_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AssemblyInstancePosition_Y { get; } + public Single[] AssemblyInstancePosition_Y { get; } public Single GetAssemblyInstancePosition_Y(int index, Single defaultValue = default) => AssemblyInstancePosition_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AssemblyInstancePosition_Z { get; } + public Single[] AssemblyInstancePosition_Z { get; } public Single GetAssemblyInstancePosition_Z(int index, Single defaultValue = default) => AssemblyInstancePosition_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AssemblyInstanceElementIndex { get; } + public int[] AssemblyInstanceElementIndex { get; } public int GetAssemblyInstanceElementIndex(int index) => AssemblyInstanceElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumAssemblyInstance => AssemblyInstanceEntityTable?.NumRows ?? 0; - public IArray AssemblyInstanceList { get; } + public AssemblyInstance[] AssemblyInstanceList { get; } public AssemblyInstance GetAssemblyInstance(int n) { if (n < 0) return null; @@ -2156,18 +2154,18 @@ public AssemblyInstance GetAssemblyInstance(int n) public EntityTable GroupEntityTable { get; } - public IArray GroupGroupType { get; } + public String[] GroupGroupType { get; } public String GetGroupGroupType(int index, String defaultValue = "") => GroupGroupType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GroupPosition_X { get; } + public Single[] GroupPosition_X { get; } public Single GetGroupPosition_X(int index, Single defaultValue = default) => GroupPosition_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GroupPosition_Y { get; } + public Single[] GroupPosition_Y { get; } public Single GetGroupPosition_Y(int index, Single defaultValue = default) => GroupPosition_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GroupPosition_Z { get; } + public Single[] GroupPosition_Z { get; } public Single GetGroupPosition_Z(int index, Single defaultValue = default) => GroupPosition_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GroupElementIndex { get; } + public int[] GroupElementIndex { get; } public int GetGroupElementIndex(int index) => GroupElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumGroup => GroupEntityTable?.NumRows ?? 0; - public IArray GroupList { get; } + public Group[] GroupList { get; } public Group GetGroup(int n) { if (n < 0) return null; @@ -2187,12 +2185,12 @@ public Group GetGroup(int n) public EntityTable DesignOptionEntityTable { get; } - public IArray DesignOptionIsPrimary { get; } + public Boolean[] DesignOptionIsPrimary { get; } public Boolean GetDesignOptionIsPrimary(int index, Boolean defaultValue = default) => DesignOptionIsPrimary?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray DesignOptionElementIndex { get; } + public int[] DesignOptionElementIndex { get; } public int GetDesignOptionElementIndex(int index) => DesignOptionElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumDesignOption => DesignOptionEntityTable?.NumRows ?? 0; - public IArray DesignOptionList { get; } + public DesignOption[] DesignOptionList { get; } public DesignOption GetDesignOption(int n) { if (n < 0) return null; @@ -2209,16 +2207,16 @@ public DesignOption GetDesignOption(int n) public EntityTable LevelEntityTable { get; } - public IArray LevelElevation { get; } + public Double[] LevelElevation { get; } public Double GetLevelElevation(int index, Double defaultValue = default) => LevelElevation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray LevelFamilyTypeIndex { get; } + public int[] LevelFamilyTypeIndex { get; } public int GetLevelFamilyTypeIndex(int index) => LevelFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray LevelBuildingIndex { get; } + public int[] LevelBuildingIndex { get; } public int GetLevelBuildingIndex(int index) => LevelBuildingIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray LevelElementIndex { get; } + public int[] LevelElementIndex { get; } public int GetLevelElementIndex(int index) => LevelElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumLevel => LevelEntityTable?.NumRows ?? 0; - public IArray LevelList { get; } + public Level[] LevelList { get; } public Level GetLevel(int n) { if (n < 0) return null; @@ -2237,10 +2235,10 @@ public Level GetLevel(int n) public EntityTable PhaseEntityTable { get; } - public IArray PhaseElementIndex { get; } + public int[] PhaseElementIndex { get; } public int GetPhaseElementIndex(int index) => PhaseElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumPhase => PhaseEntityTable?.NumRows ?? 0; - public IArray PhaseList { get; } + public Phase[] PhaseList { get; } public Phase GetPhase(int n) { if (n < 0) return null; @@ -2256,26 +2254,26 @@ public Phase GetPhase(int n) public EntityTable RoomEntityTable { get; } - public IArray RoomBaseOffset { get; } + public Double[] RoomBaseOffset { get; } public Double GetRoomBaseOffset(int index, Double defaultValue = default) => RoomBaseOffset?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray RoomLimitOffset { get; } + public Double[] RoomLimitOffset { get; } public Double GetRoomLimitOffset(int index, Double defaultValue = default) => RoomLimitOffset?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray RoomUnboundedHeight { get; } + public Double[] RoomUnboundedHeight { get; } public Double GetRoomUnboundedHeight(int index, Double defaultValue = default) => RoomUnboundedHeight?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray RoomVolume { get; } + public Double[] RoomVolume { get; } public Double GetRoomVolume(int index, Double defaultValue = default) => RoomVolume?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray RoomPerimeter { get; } + public Double[] RoomPerimeter { get; } public Double GetRoomPerimeter(int index, Double defaultValue = default) => RoomPerimeter?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray RoomArea { get; } + public Double[] RoomArea { get; } public Double GetRoomArea(int index, Double defaultValue = default) => RoomArea?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray RoomNumber { get; } + public String[] RoomNumber { get; } public String GetRoomNumber(int index, String defaultValue = "") => RoomNumber?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray RoomUpperLimitIndex { get; } + public int[] RoomUpperLimitIndex { get; } public int GetRoomUpperLimitIndex(int index) => RoomUpperLimitIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray RoomElementIndex { get; } + public int[] RoomElementIndex { get; } public int GetRoomElementIndex(int index) => RoomElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumRoom => RoomEntityTable?.NumRows ?? 0; - public IArray RoomList { get; } + public Room[] RoomList { get; } public Room GetRoom(int n) { if (n < 0) return null; @@ -2299,72 +2297,72 @@ public Room GetRoom(int n) public EntityTable BimDocumentEntityTable { get; } - public IArray BimDocumentTitle { get; } + public String[] BimDocumentTitle { get; } public String GetBimDocumentTitle(int index, String defaultValue = "") => BimDocumentTitle?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentIsMetric { get; } + public Boolean[] BimDocumentIsMetric { get; } public Boolean GetBimDocumentIsMetric(int index, Boolean defaultValue = default) => BimDocumentIsMetric?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentGuid { get; } + public String[] BimDocumentGuid { get; } public String GetBimDocumentGuid(int index, String defaultValue = "") => BimDocumentGuid?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentNumSaves { get; } + public Int32[] BimDocumentNumSaves { get; } public Int32 GetBimDocumentNumSaves(int index, Int32 defaultValue = default) => BimDocumentNumSaves?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentIsLinked { get; } + public Boolean[] BimDocumentIsLinked { get; } public Boolean GetBimDocumentIsLinked(int index, Boolean defaultValue = default) => BimDocumentIsLinked?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentIsDetached { get; } + public Boolean[] BimDocumentIsDetached { get; } public Boolean GetBimDocumentIsDetached(int index, Boolean defaultValue = default) => BimDocumentIsDetached?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentIsWorkshared { get; } + public Boolean[] BimDocumentIsWorkshared { get; } public Boolean GetBimDocumentIsWorkshared(int index, Boolean defaultValue = default) => BimDocumentIsWorkshared?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentPathName { get; } + public String[] BimDocumentPathName { get; } public String GetBimDocumentPathName(int index, String defaultValue = "") => BimDocumentPathName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentLatitude { get; } + public Double[] BimDocumentLatitude { get; } public Double GetBimDocumentLatitude(int index, Double defaultValue = default) => BimDocumentLatitude?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentLongitude { get; } + public Double[] BimDocumentLongitude { get; } public Double GetBimDocumentLongitude(int index, Double defaultValue = default) => BimDocumentLongitude?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentTimeZone { get; } + public Double[] BimDocumentTimeZone { get; } public Double GetBimDocumentTimeZone(int index, Double defaultValue = default) => BimDocumentTimeZone?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentPlaceName { get; } + public String[] BimDocumentPlaceName { get; } public String GetBimDocumentPlaceName(int index, String defaultValue = "") => BimDocumentPlaceName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentWeatherStationName { get; } + public String[] BimDocumentWeatherStationName { get; } public String GetBimDocumentWeatherStationName(int index, String defaultValue = "") => BimDocumentWeatherStationName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentElevation { get; } + public Double[] BimDocumentElevation { get; } public Double GetBimDocumentElevation(int index, Double defaultValue = default) => BimDocumentElevation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentProjectLocation { get; } + public String[] BimDocumentProjectLocation { get; } public String GetBimDocumentProjectLocation(int index, String defaultValue = "") => BimDocumentProjectLocation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentIssueDate { get; } + public String[] BimDocumentIssueDate { get; } public String GetBimDocumentIssueDate(int index, String defaultValue = "") => BimDocumentIssueDate?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentStatus { get; } + public String[] BimDocumentStatus { get; } public String GetBimDocumentStatus(int index, String defaultValue = "") => BimDocumentStatus?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentClientName { get; } + public String[] BimDocumentClientName { get; } public String GetBimDocumentClientName(int index, String defaultValue = "") => BimDocumentClientName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentAddress { get; } + public String[] BimDocumentAddress { get; } public String GetBimDocumentAddress(int index, String defaultValue = "") => BimDocumentAddress?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentName { get; } + public String[] BimDocumentName { get; } public String GetBimDocumentName(int index, String defaultValue = "") => BimDocumentName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentNumber { get; } + public String[] BimDocumentNumber { get; } public String GetBimDocumentNumber(int index, String defaultValue = "") => BimDocumentNumber?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentAuthor { get; } + public String[] BimDocumentAuthor { get; } public String GetBimDocumentAuthor(int index, String defaultValue = "") => BimDocumentAuthor?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentBuildingName { get; } + public String[] BimDocumentBuildingName { get; } public String GetBimDocumentBuildingName(int index, String defaultValue = "") => BimDocumentBuildingName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentOrganizationName { get; } + public String[] BimDocumentOrganizationName { get; } public String GetBimDocumentOrganizationName(int index, String defaultValue = "") => BimDocumentOrganizationName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentOrganizationDescription { get; } + public String[] BimDocumentOrganizationDescription { get; } public String GetBimDocumentOrganizationDescription(int index, String defaultValue = "") => BimDocumentOrganizationDescription?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentProduct { get; } + public String[] BimDocumentProduct { get; } public String GetBimDocumentProduct(int index, String defaultValue = "") => BimDocumentProduct?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentVersion { get; } + public String[] BimDocumentVersion { get; } public String GetBimDocumentVersion(int index, String defaultValue = "") => BimDocumentVersion?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentUser { get; } + public String[] BimDocumentUser { get; } public String GetBimDocumentUser(int index, String defaultValue = "") => BimDocumentUser?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentActiveViewIndex { get; } + public int[] BimDocumentActiveViewIndex { get; } public int GetBimDocumentActiveViewIndex(int index) => BimDocumentActiveViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray BimDocumentOwnerFamilyIndex { get; } + public int[] BimDocumentOwnerFamilyIndex { get; } public int GetBimDocumentOwnerFamilyIndex(int index) => BimDocumentOwnerFamilyIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray BimDocumentParentIndex { get; } + public int[] BimDocumentParentIndex { get; } public int GetBimDocumentParentIndex(int index) => BimDocumentParentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray BimDocumentElementIndex { get; } + public int[] BimDocumentElementIndex { get; } public int GetBimDocumentElementIndex(int index) => BimDocumentElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumBimDocument => BimDocumentEntityTable?.NumRows ?? 0; - public IArray BimDocumentList { get; } + public BimDocument[] BimDocumentList { get; } public BimDocument GetBimDocument(int n) { if (n < 0) return null; @@ -2411,12 +2409,12 @@ public BimDocument GetBimDocument(int n) public EntityTable DisplayUnitInBimDocumentEntityTable { get; } - public IArray DisplayUnitInBimDocumentDisplayUnitIndex { get; } + public int[] DisplayUnitInBimDocumentDisplayUnitIndex { get; } public int GetDisplayUnitInBimDocumentDisplayUnitIndex(int index) => DisplayUnitInBimDocumentDisplayUnitIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray DisplayUnitInBimDocumentBimDocumentIndex { get; } + public int[] DisplayUnitInBimDocumentBimDocumentIndex { get; } public int GetDisplayUnitInBimDocumentBimDocumentIndex(int index) => DisplayUnitInBimDocumentBimDocumentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumDisplayUnitInBimDocument => DisplayUnitInBimDocumentEntityTable?.NumRows ?? 0; - public IArray DisplayUnitInBimDocumentList { get; } + public DisplayUnitInBimDocument[] DisplayUnitInBimDocumentList { get; } public DisplayUnitInBimDocument GetDisplayUnitInBimDocument(int n) { if (n < 0) return null; @@ -2433,14 +2431,14 @@ public DisplayUnitInBimDocument GetDisplayUnitInBimDocument(int n) public EntityTable PhaseOrderInBimDocumentEntityTable { get; } - public IArray PhaseOrderInBimDocumentOrderIndex { get; } + public Int32[] PhaseOrderInBimDocumentOrderIndex { get; } public Int32 GetPhaseOrderInBimDocumentOrderIndex(int index, Int32 defaultValue = default) => PhaseOrderInBimDocumentOrderIndex?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray PhaseOrderInBimDocumentPhaseIndex { get; } + public int[] PhaseOrderInBimDocumentPhaseIndex { get; } public int GetPhaseOrderInBimDocumentPhaseIndex(int index) => PhaseOrderInBimDocumentPhaseIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray PhaseOrderInBimDocumentBimDocumentIndex { get; } + public int[] PhaseOrderInBimDocumentBimDocumentIndex { get; } public int GetPhaseOrderInBimDocumentBimDocumentIndex(int index) => PhaseOrderInBimDocumentBimDocumentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumPhaseOrderInBimDocument => PhaseOrderInBimDocumentEntityTable?.NumRows ?? 0; - public IArray PhaseOrderInBimDocumentList { get; } + public PhaseOrderInBimDocument[] PhaseOrderInBimDocumentList { get; } public PhaseOrderInBimDocument GetPhaseOrderInBimDocument(int n) { if (n < 0) return null; @@ -2458,26 +2456,26 @@ public PhaseOrderInBimDocument GetPhaseOrderInBimDocument(int n) public EntityTable CategoryEntityTable { get; } - public IArray CategoryName { get; } + public String[] CategoryName { get; } public String GetCategoryName(int index, String defaultValue = "") => CategoryName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CategoryId { get; } + public Int64[] CategoryId { get; } public Int64 GetCategoryId(int index, Int64 defaultValue = default) => CategoryId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CategoryCategoryType { get; } + public String[] CategoryCategoryType { get; } public String GetCategoryCategoryType(int index, String defaultValue = "") => CategoryCategoryType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CategoryLineColor_X { get; } + public Double[] CategoryLineColor_X { get; } public Double GetCategoryLineColor_X(int index, Double defaultValue = default) => CategoryLineColor_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CategoryLineColor_Y { get; } + public Double[] CategoryLineColor_Y { get; } public Double GetCategoryLineColor_Y(int index, Double defaultValue = default) => CategoryLineColor_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CategoryLineColor_Z { get; } + public Double[] CategoryLineColor_Z { get; } public Double GetCategoryLineColor_Z(int index, Double defaultValue = default) => CategoryLineColor_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CategoryBuiltInCategory { get; } + public String[] CategoryBuiltInCategory { get; } public String GetCategoryBuiltInCategory(int index, String defaultValue = "") => CategoryBuiltInCategory?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CategoryParentIndex { get; } + public int[] CategoryParentIndex { get; } public int GetCategoryParentIndex(int index) => CategoryParentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray CategoryMaterialIndex { get; } + public int[] CategoryMaterialIndex { get; } public int GetCategoryMaterialIndex(int index) => CategoryMaterialIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumCategory => CategoryEntityTable?.NumRows ?? 0; - public IArray CategoryList { get; } + public Category[] CategoryList { get; } public Category GetCategory(int n) { if (n < 0) return null; @@ -2501,20 +2499,20 @@ public Category GetCategory(int n) public EntityTable FamilyEntityTable { get; } - public IArray FamilyStructuralMaterialType { get; } + public String[] FamilyStructuralMaterialType { get; } public String GetFamilyStructuralMaterialType(int index, String defaultValue = "") => FamilyStructuralMaterialType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyStructuralSectionShape { get; } + public String[] FamilyStructuralSectionShape { get; } public String GetFamilyStructuralSectionShape(int index, String defaultValue = "") => FamilyStructuralSectionShape?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyIsSystemFamily { get; } + public Boolean[] FamilyIsSystemFamily { get; } public Boolean GetFamilyIsSystemFamily(int index, Boolean defaultValue = default) => FamilyIsSystemFamily?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyIsInPlace { get; } + public Boolean[] FamilyIsInPlace { get; } public Boolean GetFamilyIsInPlace(int index, Boolean defaultValue = default) => FamilyIsInPlace?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyFamilyCategoryIndex { get; } + public int[] FamilyFamilyCategoryIndex { get; } public int GetFamilyFamilyCategoryIndex(int index) => FamilyFamilyCategoryIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyElementIndex { get; } + public int[] FamilyElementIndex { get; } public int GetFamilyElementIndex(int index) => FamilyElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumFamily => FamilyEntityTable?.NumRows ?? 0; - public IArray FamilyList { get; } + public Family[] FamilyList { get; } public Family GetFamily(int n) { if (n < 0) return null; @@ -2535,16 +2533,16 @@ public Family GetFamily(int n) public EntityTable FamilyTypeEntityTable { get; } - public IArray FamilyTypeIsSystemFamilyType { get; } + public Boolean[] FamilyTypeIsSystemFamilyType { get; } public Boolean GetFamilyTypeIsSystemFamilyType(int index, Boolean defaultValue = default) => FamilyTypeIsSystemFamilyType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyTypeFamilyIndex { get; } + public int[] FamilyTypeFamilyIndex { get; } public int GetFamilyTypeFamilyIndex(int index) => FamilyTypeFamilyIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyTypeCompoundStructureIndex { get; } + public int[] FamilyTypeCompoundStructureIndex { get; } public int GetFamilyTypeCompoundStructureIndex(int index) => FamilyTypeCompoundStructureIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyTypeElementIndex { get; } + public int[] FamilyTypeElementIndex { get; } public int GetFamilyTypeElementIndex(int index) => FamilyTypeElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumFamilyType => FamilyTypeEntityTable?.NumRows ?? 0; - public IArray FamilyTypeList { get; } + public FamilyType[] FamilyTypeList { get; } public FamilyType GetFamilyType(int n) { if (n < 0) return null; @@ -2563,66 +2561,66 @@ public FamilyType GetFamilyType(int n) public EntityTable FamilyInstanceEntityTable { get; } - public IArray FamilyInstanceFacingFlipped { get; } + public Boolean[] FamilyInstanceFacingFlipped { get; } public Boolean GetFamilyInstanceFacingFlipped(int index, Boolean defaultValue = default) => FamilyInstanceFacingFlipped?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceFacingOrientation_X { get; } + public Single[] FamilyInstanceFacingOrientation_X { get; } public Single GetFamilyInstanceFacingOrientation_X(int index, Single defaultValue = default) => FamilyInstanceFacingOrientation_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceFacingOrientation_Y { get; } + public Single[] FamilyInstanceFacingOrientation_Y { get; } public Single GetFamilyInstanceFacingOrientation_Y(int index, Single defaultValue = default) => FamilyInstanceFacingOrientation_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceFacingOrientation_Z { get; } + public Single[] FamilyInstanceFacingOrientation_Z { get; } public Single GetFamilyInstanceFacingOrientation_Z(int index, Single defaultValue = default) => FamilyInstanceFacingOrientation_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceHandFlipped { get; } + public Boolean[] FamilyInstanceHandFlipped { get; } public Boolean GetFamilyInstanceHandFlipped(int index, Boolean defaultValue = default) => FamilyInstanceHandFlipped?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceMirrored { get; } + public Boolean[] FamilyInstanceMirrored { get; } public Boolean GetFamilyInstanceMirrored(int index, Boolean defaultValue = default) => FamilyInstanceMirrored?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceHasModifiedGeometry { get; } + public Boolean[] FamilyInstanceHasModifiedGeometry { get; } public Boolean GetFamilyInstanceHasModifiedGeometry(int index, Boolean defaultValue = default) => FamilyInstanceHasModifiedGeometry?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceScale { get; } + public Single[] FamilyInstanceScale { get; } public Single GetFamilyInstanceScale(int index, Single defaultValue = default) => FamilyInstanceScale?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisX_X { get; } + public Single[] FamilyInstanceBasisX_X { get; } public Single GetFamilyInstanceBasisX_X(int index, Single defaultValue = default) => FamilyInstanceBasisX_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisX_Y { get; } + public Single[] FamilyInstanceBasisX_Y { get; } public Single GetFamilyInstanceBasisX_Y(int index, Single defaultValue = default) => FamilyInstanceBasisX_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisX_Z { get; } + public Single[] FamilyInstanceBasisX_Z { get; } public Single GetFamilyInstanceBasisX_Z(int index, Single defaultValue = default) => FamilyInstanceBasisX_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisY_X { get; } + public Single[] FamilyInstanceBasisY_X { get; } public Single GetFamilyInstanceBasisY_X(int index, Single defaultValue = default) => FamilyInstanceBasisY_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisY_Y { get; } + public Single[] FamilyInstanceBasisY_Y { get; } public Single GetFamilyInstanceBasisY_Y(int index, Single defaultValue = default) => FamilyInstanceBasisY_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisY_Z { get; } + public Single[] FamilyInstanceBasisY_Z { get; } public Single GetFamilyInstanceBasisY_Z(int index, Single defaultValue = default) => FamilyInstanceBasisY_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisZ_X { get; } + public Single[] FamilyInstanceBasisZ_X { get; } public Single GetFamilyInstanceBasisZ_X(int index, Single defaultValue = default) => FamilyInstanceBasisZ_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisZ_Y { get; } + public Single[] FamilyInstanceBasisZ_Y { get; } public Single GetFamilyInstanceBasisZ_Y(int index, Single defaultValue = default) => FamilyInstanceBasisZ_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisZ_Z { get; } + public Single[] FamilyInstanceBasisZ_Z { get; } public Single GetFamilyInstanceBasisZ_Z(int index, Single defaultValue = default) => FamilyInstanceBasisZ_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceTranslation_X { get; } + public Single[] FamilyInstanceTranslation_X { get; } public Single GetFamilyInstanceTranslation_X(int index, Single defaultValue = default) => FamilyInstanceTranslation_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceTranslation_Y { get; } + public Single[] FamilyInstanceTranslation_Y { get; } public Single GetFamilyInstanceTranslation_Y(int index, Single defaultValue = default) => FamilyInstanceTranslation_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceTranslation_Z { get; } + public Single[] FamilyInstanceTranslation_Z { get; } public Single GetFamilyInstanceTranslation_Z(int index, Single defaultValue = default) => FamilyInstanceTranslation_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceHandOrientation_X { get; } + public Single[] FamilyInstanceHandOrientation_X { get; } public Single GetFamilyInstanceHandOrientation_X(int index, Single defaultValue = default) => FamilyInstanceHandOrientation_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceHandOrientation_Y { get; } + public Single[] FamilyInstanceHandOrientation_Y { get; } public Single GetFamilyInstanceHandOrientation_Y(int index, Single defaultValue = default) => FamilyInstanceHandOrientation_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceHandOrientation_Z { get; } + public Single[] FamilyInstanceHandOrientation_Z { get; } public Single GetFamilyInstanceHandOrientation_Z(int index, Single defaultValue = default) => FamilyInstanceHandOrientation_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceFamilyTypeIndex { get; } + public int[] FamilyInstanceFamilyTypeIndex { get; } public int GetFamilyInstanceFamilyTypeIndex(int index) => FamilyInstanceFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyInstanceHostIndex { get; } + public int[] FamilyInstanceHostIndex { get; } public int GetFamilyInstanceHostIndex(int index) => FamilyInstanceHostIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyInstanceFromRoomIndex { get; } + public int[] FamilyInstanceFromRoomIndex { get; } public int GetFamilyInstanceFromRoomIndex(int index) => FamilyInstanceFromRoomIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyInstanceToRoomIndex { get; } + public int[] FamilyInstanceToRoomIndex { get; } public int GetFamilyInstanceToRoomIndex(int index) => FamilyInstanceToRoomIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyInstanceSuperComponentIndex { get; } + public int[] FamilyInstanceSuperComponentIndex { get; } public int GetFamilyInstanceSuperComponentIndex(int index) => FamilyInstanceSuperComponentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyInstanceElementIndex { get; } + public int[] FamilyInstanceElementIndex { get; } public int GetFamilyInstanceElementIndex(int index) => FamilyInstanceElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumFamilyInstance => FamilyInstanceEntityTable?.NumRows ?? 0; - public IArray FamilyInstanceList { get; } + public FamilyInstance[] FamilyInstanceList { get; } public FamilyInstance GetFamilyInstance(int n) { if (n < 0) return null; @@ -2666,60 +2664,60 @@ public FamilyInstance GetFamilyInstance(int n) public EntityTable ViewEntityTable { get; } - public IArray ViewTitle { get; } + public String[] ViewTitle { get; } public String GetViewTitle(int index, String defaultValue = "") => ViewTitle?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewViewType { get; } + public String[] ViewViewType { get; } public String GetViewViewType(int index, String defaultValue = "") => ViewViewType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewUp_X { get; } + public Double[] ViewUp_X { get; } public Double GetViewUp_X(int index, Double defaultValue = default) => ViewUp_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewUp_Y { get; } + public Double[] ViewUp_Y { get; } public Double GetViewUp_Y(int index, Double defaultValue = default) => ViewUp_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewUp_Z { get; } + public Double[] ViewUp_Z { get; } public Double GetViewUp_Z(int index, Double defaultValue = default) => ViewUp_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewRight_X { get; } + public Double[] ViewRight_X { get; } public Double GetViewRight_X(int index, Double defaultValue = default) => ViewRight_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewRight_Y { get; } + public Double[] ViewRight_Y { get; } public Double GetViewRight_Y(int index, Double defaultValue = default) => ViewRight_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewRight_Z { get; } + public Double[] ViewRight_Z { get; } public Double GetViewRight_Z(int index, Double defaultValue = default) => ViewRight_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewOrigin_X { get; } + public Double[] ViewOrigin_X { get; } public Double GetViewOrigin_X(int index, Double defaultValue = default) => ViewOrigin_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewOrigin_Y { get; } + public Double[] ViewOrigin_Y { get; } public Double GetViewOrigin_Y(int index, Double defaultValue = default) => ViewOrigin_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewOrigin_Z { get; } + public Double[] ViewOrigin_Z { get; } public Double GetViewOrigin_Z(int index, Double defaultValue = default) => ViewOrigin_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewViewDirection_X { get; } + public Double[] ViewViewDirection_X { get; } public Double GetViewViewDirection_X(int index, Double defaultValue = default) => ViewViewDirection_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewViewDirection_Y { get; } + public Double[] ViewViewDirection_Y { get; } public Double GetViewViewDirection_Y(int index, Double defaultValue = default) => ViewViewDirection_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewViewDirection_Z { get; } + public Double[] ViewViewDirection_Z { get; } public Double GetViewViewDirection_Z(int index, Double defaultValue = default) => ViewViewDirection_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewViewPosition_X { get; } + public Double[] ViewViewPosition_X { get; } public Double GetViewViewPosition_X(int index, Double defaultValue = default) => ViewViewPosition_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewViewPosition_Y { get; } + public Double[] ViewViewPosition_Y { get; } public Double GetViewViewPosition_Y(int index, Double defaultValue = default) => ViewViewPosition_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewViewPosition_Z { get; } + public Double[] ViewViewPosition_Z { get; } public Double GetViewViewPosition_Z(int index, Double defaultValue = default) => ViewViewPosition_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewScale { get; } + public Double[] ViewScale { get; } public Double GetViewScale(int index, Double defaultValue = default) => ViewScale?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewOutline_Min_X { get; } + public Double[] ViewOutline_Min_X { get; } public Double GetViewOutline_Min_X(int index, Double defaultValue = default) => ViewOutline_Min_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewOutline_Min_Y { get; } + public Double[] ViewOutline_Min_Y { get; } public Double GetViewOutline_Min_Y(int index, Double defaultValue = default) => ViewOutline_Min_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewOutline_Max_X { get; } + public Double[] ViewOutline_Max_X { get; } public Double GetViewOutline_Max_X(int index, Double defaultValue = default) => ViewOutline_Max_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewOutline_Max_Y { get; } + public Double[] ViewOutline_Max_Y { get; } public Double GetViewOutline_Max_Y(int index, Double defaultValue = default) => ViewOutline_Max_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewDetailLevel { get; } + public Int32[] ViewDetailLevel { get; } public Int32 GetViewDetailLevel(int index, Int32 defaultValue = default) => ViewDetailLevel?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewCameraIndex { get; } + public int[] ViewCameraIndex { get; } public int GetViewCameraIndex(int index) => ViewCameraIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ViewFamilyTypeIndex { get; } + public int[] ViewFamilyTypeIndex { get; } public int GetViewFamilyTypeIndex(int index) => ViewFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ViewElementIndex { get; } + public int[] ViewElementIndex { get; } public int GetViewElementIndex(int index) => ViewElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumView => ViewEntityTable?.NumRows ?? 0; - public IArray ViewList { get; } + public View[] ViewList { get; } public View GetView(int n) { if (n < 0) return null; @@ -2760,12 +2758,12 @@ public View GetView(int n) public EntityTable ElementInViewEntityTable { get; } - public IArray ElementInViewViewIndex { get; } + public int[] ElementInViewViewIndex { get; } public int GetElementInViewViewIndex(int index) => ElementInViewViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementInViewElementIndex { get; } + public int[] ElementInViewElementIndex { get; } public int GetElementInViewElementIndex(int index) => ElementInViewElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumElementInView => ElementInViewEntityTable?.NumRows ?? 0; - public IArray ElementInViewList { get; } + public ElementInView[] ElementInViewList { get; } public ElementInView GetElementInView(int n) { if (n < 0) return null; @@ -2782,12 +2780,12 @@ public ElementInView GetElementInView(int n) public EntityTable ShapeInViewEntityTable { get; } - public IArray ShapeInViewShapeIndex { get; } + public int[] ShapeInViewShapeIndex { get; } public int GetShapeInViewShapeIndex(int index) => ShapeInViewShapeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ShapeInViewViewIndex { get; } + public int[] ShapeInViewViewIndex { get; } public int GetShapeInViewViewIndex(int index) => ShapeInViewViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumShapeInView => ShapeInViewEntityTable?.NumRows ?? 0; - public IArray ShapeInViewList { get; } + public ShapeInView[] ShapeInViewList { get; } public ShapeInView GetShapeInView(int n) { if (n < 0) return null; @@ -2804,12 +2802,12 @@ public ShapeInView GetShapeInView(int n) public EntityTable AssetInViewEntityTable { get; } - public IArray AssetInViewAssetIndex { get; } + public int[] AssetInViewAssetIndex { get; } public int GetAssetInViewAssetIndex(int index) => AssetInViewAssetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray AssetInViewViewIndex { get; } + public int[] AssetInViewViewIndex { get; } public int GetAssetInViewViewIndex(int index) => AssetInViewViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumAssetInView => AssetInViewEntityTable?.NumRows ?? 0; - public IArray AssetInViewList { get; } + public AssetInView[] AssetInViewList { get; } public AssetInView GetAssetInView(int n) { if (n < 0) return null; @@ -2826,12 +2824,12 @@ public AssetInView GetAssetInView(int n) public EntityTable AssetInViewSheetEntityTable { get; } - public IArray AssetInViewSheetAssetIndex { get; } + public int[] AssetInViewSheetAssetIndex { get; } public int GetAssetInViewSheetAssetIndex(int index) => AssetInViewSheetAssetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray AssetInViewSheetViewSheetIndex { get; } + public int[] AssetInViewSheetViewSheetIndex { get; } public int GetAssetInViewSheetViewSheetIndex(int index) => AssetInViewSheetViewSheetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumAssetInViewSheet => AssetInViewSheetEntityTable?.NumRows ?? 0; - public IArray AssetInViewSheetList { get; } + public AssetInViewSheet[] AssetInViewSheetList { get; } public AssetInViewSheet GetAssetInViewSheet(int n) { if (n < 0) return null; @@ -2848,24 +2846,24 @@ public AssetInViewSheet GetAssetInViewSheet(int n) public EntityTable LevelInViewEntityTable { get; } - public IArray LevelInViewExtents_Min_X { get; } + public Double[] LevelInViewExtents_Min_X { get; } public Double GetLevelInViewExtents_Min_X(int index, Double defaultValue = default) => LevelInViewExtents_Min_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray LevelInViewExtents_Min_Y { get; } + public Double[] LevelInViewExtents_Min_Y { get; } public Double GetLevelInViewExtents_Min_Y(int index, Double defaultValue = default) => LevelInViewExtents_Min_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray LevelInViewExtents_Min_Z { get; } + public Double[] LevelInViewExtents_Min_Z { get; } public Double GetLevelInViewExtents_Min_Z(int index, Double defaultValue = default) => LevelInViewExtents_Min_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray LevelInViewExtents_Max_X { get; } + public Double[] LevelInViewExtents_Max_X { get; } public Double GetLevelInViewExtents_Max_X(int index, Double defaultValue = default) => LevelInViewExtents_Max_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray LevelInViewExtents_Max_Y { get; } + public Double[] LevelInViewExtents_Max_Y { get; } public Double GetLevelInViewExtents_Max_Y(int index, Double defaultValue = default) => LevelInViewExtents_Max_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray LevelInViewExtents_Max_Z { get; } + public Double[] LevelInViewExtents_Max_Z { get; } public Double GetLevelInViewExtents_Max_Z(int index, Double defaultValue = default) => LevelInViewExtents_Max_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray LevelInViewLevelIndex { get; } + public int[] LevelInViewLevelIndex { get; } public int GetLevelInViewLevelIndex(int index) => LevelInViewLevelIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray LevelInViewViewIndex { get; } + public int[] LevelInViewViewIndex { get; } public int GetLevelInViewViewIndex(int index) => LevelInViewViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumLevelInView => LevelInViewEntityTable?.NumRows ?? 0; - public IArray LevelInViewList { get; } + public LevelInView[] LevelInViewList { get; } public LevelInView GetLevelInView(int n) { if (n < 0) return null; @@ -2888,26 +2886,26 @@ public LevelInView GetLevelInView(int n) public EntityTable CameraEntityTable { get; } - public IArray CameraId { get; } + public Int32[] CameraId { get; } public Int32 GetCameraId(int index, Int32 defaultValue = default) => CameraId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraIsPerspective { get; } + public Int32[] CameraIsPerspective { get; } public Int32 GetCameraIsPerspective(int index, Int32 defaultValue = default) => CameraIsPerspective?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraVerticalExtent { get; } + public Double[] CameraVerticalExtent { get; } public Double GetCameraVerticalExtent(int index, Double defaultValue = default) => CameraVerticalExtent?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraHorizontalExtent { get; } + public Double[] CameraHorizontalExtent { get; } public Double GetCameraHorizontalExtent(int index, Double defaultValue = default) => CameraHorizontalExtent?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraFarDistance { get; } + public Double[] CameraFarDistance { get; } public Double GetCameraFarDistance(int index, Double defaultValue = default) => CameraFarDistance?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraNearDistance { get; } + public Double[] CameraNearDistance { get; } public Double GetCameraNearDistance(int index, Double defaultValue = default) => CameraNearDistance?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraTargetDistance { get; } + public Double[] CameraTargetDistance { get; } public Double GetCameraTargetDistance(int index, Double defaultValue = default) => CameraTargetDistance?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraRightOffset { get; } + public Double[] CameraRightOffset { get; } public Double GetCameraRightOffset(int index, Double defaultValue = default) => CameraRightOffset?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraUpOffset { get; } + public Double[] CameraUpOffset { get; } public Double GetCameraUpOffset(int index, Double defaultValue = default) => CameraUpOffset?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; public int NumCamera => CameraEntityTable?.NumRows ?? 0; - public IArray CameraList { get; } + public Camera[] CameraList { get; } public Camera GetCamera(int n) { if (n < 0) return null; @@ -2931,48 +2929,48 @@ public Camera GetCamera(int n) public EntityTable MaterialEntityTable { get; } - public IArray MaterialName { get; } + public String[] MaterialName { get; } public String GetMaterialName(int index, String defaultValue = "") => MaterialName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialMaterialCategory { get; } + public String[] MaterialMaterialCategory { get; } public String GetMaterialMaterialCategory(int index, String defaultValue = "") => MaterialMaterialCategory?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColor_X { get; } + public Double[] MaterialColor_X { get; } public Double GetMaterialColor_X(int index, Double defaultValue = default) => MaterialColor_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColor_Y { get; } + public Double[] MaterialColor_Y { get; } public Double GetMaterialColor_Y(int index, Double defaultValue = default) => MaterialColor_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColor_Z { get; } + public Double[] MaterialColor_Z { get; } public Double GetMaterialColor_Z(int index, Double defaultValue = default) => MaterialColor_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColorUvScaling_X { get; } + public Double[] MaterialColorUvScaling_X { get; } public Double GetMaterialColorUvScaling_X(int index, Double defaultValue = default) => MaterialColorUvScaling_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColorUvScaling_Y { get; } + public Double[] MaterialColorUvScaling_Y { get; } public Double GetMaterialColorUvScaling_Y(int index, Double defaultValue = default) => MaterialColorUvScaling_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColorUvOffset_X { get; } + public Double[] MaterialColorUvOffset_X { get; } public Double GetMaterialColorUvOffset_X(int index, Double defaultValue = default) => MaterialColorUvOffset_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColorUvOffset_Y { get; } + public Double[] MaterialColorUvOffset_Y { get; } public Double GetMaterialColorUvOffset_Y(int index, Double defaultValue = default) => MaterialColorUvOffset_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialNormalUvScaling_X { get; } + public Double[] MaterialNormalUvScaling_X { get; } public Double GetMaterialNormalUvScaling_X(int index, Double defaultValue = default) => MaterialNormalUvScaling_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialNormalUvScaling_Y { get; } + public Double[] MaterialNormalUvScaling_Y { get; } public Double GetMaterialNormalUvScaling_Y(int index, Double defaultValue = default) => MaterialNormalUvScaling_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialNormalUvOffset_X { get; } + public Double[] MaterialNormalUvOffset_X { get; } public Double GetMaterialNormalUvOffset_X(int index, Double defaultValue = default) => MaterialNormalUvOffset_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialNormalUvOffset_Y { get; } + public Double[] MaterialNormalUvOffset_Y { get; } public Double GetMaterialNormalUvOffset_Y(int index, Double defaultValue = default) => MaterialNormalUvOffset_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialNormalAmount { get; } + public Double[] MaterialNormalAmount { get; } public Double GetMaterialNormalAmount(int index, Double defaultValue = default) => MaterialNormalAmount?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialGlossiness { get; } + public Double[] MaterialGlossiness { get; } public Double GetMaterialGlossiness(int index, Double defaultValue = default) => MaterialGlossiness?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialSmoothness { get; } + public Double[] MaterialSmoothness { get; } public Double GetMaterialSmoothness(int index, Double defaultValue = default) => MaterialSmoothness?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialTransparency { get; } + public Double[] MaterialTransparency { get; } public Double GetMaterialTransparency(int index, Double defaultValue = default) => MaterialTransparency?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColorTextureFileIndex { get; } + public int[] MaterialColorTextureFileIndex { get; } public int GetMaterialColorTextureFileIndex(int index) => MaterialColorTextureFileIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray MaterialNormalTextureFileIndex { get; } + public int[] MaterialNormalTextureFileIndex { get; } public int GetMaterialNormalTextureFileIndex(int index) => MaterialNormalTextureFileIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray MaterialElementIndex { get; } + public int[] MaterialElementIndex { get; } public int GetMaterialElementIndex(int index) => MaterialElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumMaterial => MaterialEntityTable?.NumRows ?? 0; - public IArray MaterialList { get; } + public Material[] MaterialList { get; } public Material GetMaterial(int n) { if (n < 0) return null; @@ -3007,18 +3005,18 @@ public Material GetMaterial(int n) public EntityTable MaterialInElementEntityTable { get; } - public IArray MaterialInElementArea { get; } + public Double[] MaterialInElementArea { get; } public Double GetMaterialInElementArea(int index, Double defaultValue = default) => MaterialInElementArea?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialInElementVolume { get; } + public Double[] MaterialInElementVolume { get; } public Double GetMaterialInElementVolume(int index, Double defaultValue = default) => MaterialInElementVolume?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialInElementIsPaint { get; } + public Boolean[] MaterialInElementIsPaint { get; } public Boolean GetMaterialInElementIsPaint(int index, Boolean defaultValue = default) => MaterialInElementIsPaint?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialInElementMaterialIndex { get; } + public int[] MaterialInElementMaterialIndex { get; } public int GetMaterialInElementMaterialIndex(int index) => MaterialInElementMaterialIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray MaterialInElementElementIndex { get; } + public int[] MaterialInElementElementIndex { get; } public int GetMaterialInElementElementIndex(int index) => MaterialInElementElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumMaterialInElement => MaterialInElementEntityTable?.NumRows ?? 0; - public IArray MaterialInElementList { get; } + public MaterialInElement[] MaterialInElementList { get; } public MaterialInElement GetMaterialInElement(int n) { if (n < 0) return null; @@ -3038,18 +3036,18 @@ public MaterialInElement GetMaterialInElement(int n) public EntityTable CompoundStructureLayerEntityTable { get; } - public IArray CompoundStructureLayerOrderIndex { get; } + public Int32[] CompoundStructureLayerOrderIndex { get; } public Int32 GetCompoundStructureLayerOrderIndex(int index, Int32 defaultValue = default) => CompoundStructureLayerOrderIndex?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CompoundStructureLayerWidth { get; } + public Double[] CompoundStructureLayerWidth { get; } public Double GetCompoundStructureLayerWidth(int index, Double defaultValue = default) => CompoundStructureLayerWidth?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CompoundStructureLayerMaterialFunctionAssignment { get; } + public String[] CompoundStructureLayerMaterialFunctionAssignment { get; } public String GetCompoundStructureLayerMaterialFunctionAssignment(int index, String defaultValue = "") => CompoundStructureLayerMaterialFunctionAssignment?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CompoundStructureLayerMaterialIndex { get; } + public int[] CompoundStructureLayerMaterialIndex { get; } public int GetCompoundStructureLayerMaterialIndex(int index) => CompoundStructureLayerMaterialIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray CompoundStructureLayerCompoundStructureIndex { get; } + public int[] CompoundStructureLayerCompoundStructureIndex { get; } public int GetCompoundStructureLayerCompoundStructureIndex(int index) => CompoundStructureLayerCompoundStructureIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumCompoundStructureLayer => CompoundStructureLayerEntityTable?.NumRows ?? 0; - public IArray CompoundStructureLayerList { get; } + public CompoundStructureLayer[] CompoundStructureLayerList { get; } public CompoundStructureLayer GetCompoundStructureLayer(int n) { if (n < 0) return null; @@ -3069,12 +3067,12 @@ public CompoundStructureLayer GetCompoundStructureLayer(int n) public EntityTable CompoundStructureEntityTable { get; } - public IArray CompoundStructureWidth { get; } + public Double[] CompoundStructureWidth { get; } public Double GetCompoundStructureWidth(int index, Double defaultValue = default) => CompoundStructureWidth?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CompoundStructureStructuralLayerIndex { get; } + public int[] CompoundStructureStructuralLayerIndex { get; } public int GetCompoundStructureStructuralLayerIndex(int index) => CompoundStructureStructuralLayerIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumCompoundStructure => CompoundStructureEntityTable?.NumRows ?? 0; - public IArray CompoundStructureList { get; } + public CompoundStructure[] CompoundStructureList { get; } public CompoundStructure GetCompoundStructure(int n) { if (n < 0) return null; @@ -3091,10 +3089,10 @@ public CompoundStructure GetCompoundStructure(int n) public EntityTable NodeEntityTable { get; } - public IArray NodeElementIndex { get; } + public int[] NodeElementIndex { get; } public int GetNodeElementIndex(int index) => NodeElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumNode => NodeEntityTable?.NumRows ?? 0; - public IArray NodeList { get; } + public Node[] NodeList { get; } public Node GetNode(int n) { if (n < 0) return null; @@ -3110,24 +3108,24 @@ public Node GetNode(int n) public EntityTable GeometryEntityTable { get; } - public IArray GeometryBox_Min_X { get; } + public Single[] GeometryBox_Min_X { get; } public Single GetGeometryBox_Min_X(int index, Single defaultValue = default) => GeometryBox_Min_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GeometryBox_Min_Y { get; } + public Single[] GeometryBox_Min_Y { get; } public Single GetGeometryBox_Min_Y(int index, Single defaultValue = default) => GeometryBox_Min_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GeometryBox_Min_Z { get; } + public Single[] GeometryBox_Min_Z { get; } public Single GetGeometryBox_Min_Z(int index, Single defaultValue = default) => GeometryBox_Min_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GeometryBox_Max_X { get; } + public Single[] GeometryBox_Max_X { get; } public Single GetGeometryBox_Max_X(int index, Single defaultValue = default) => GeometryBox_Max_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GeometryBox_Max_Y { get; } + public Single[] GeometryBox_Max_Y { get; } public Single GetGeometryBox_Max_Y(int index, Single defaultValue = default) => GeometryBox_Max_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GeometryBox_Max_Z { get; } + public Single[] GeometryBox_Max_Z { get; } public Single GetGeometryBox_Max_Z(int index, Single defaultValue = default) => GeometryBox_Max_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GeometryVertexCount { get; } + public Int32[] GeometryVertexCount { get; } public Int32 GetGeometryVertexCount(int index, Int32 defaultValue = default) => GeometryVertexCount?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GeometryFaceCount { get; } + public Int32[] GeometryFaceCount { get; } public Int32 GetGeometryFaceCount(int index, Int32 defaultValue = default) => GeometryFaceCount?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; public int NumGeometry => GeometryEntityTable?.NumRows ?? 0; - public IArray GeometryList { get; } + public Geometry[] GeometryList { get; } public Geometry GetGeometry(int n) { if (n < 0) return null; @@ -3150,10 +3148,10 @@ public Geometry GetGeometry(int n) public EntityTable ShapeEntityTable { get; } - public IArray ShapeElementIndex { get; } + public int[] ShapeElementIndex { get; } public int GetShapeElementIndex(int index) => ShapeElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumShape => ShapeEntityTable?.NumRows ?? 0; - public IArray ShapeList { get; } + public Shape[] ShapeList { get; } public Shape GetShape(int n) { if (n < 0) return null; @@ -3169,10 +3167,10 @@ public Shape GetShape(int n) public EntityTable ShapeCollectionEntityTable { get; } - public IArray ShapeCollectionElementIndex { get; } + public int[] ShapeCollectionElementIndex { get; } public int GetShapeCollectionElementIndex(int index) => ShapeCollectionElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumShapeCollection => ShapeCollectionEntityTable?.NumRows ?? 0; - public IArray ShapeCollectionList { get; } + public ShapeCollection[] ShapeCollectionList { get; } public ShapeCollection GetShapeCollection(int n) { if (n < 0) return null; @@ -3188,12 +3186,12 @@ public ShapeCollection GetShapeCollection(int n) public EntityTable ShapeInShapeCollectionEntityTable { get; } - public IArray ShapeInShapeCollectionShapeIndex { get; } + public int[] ShapeInShapeCollectionShapeIndex { get; } public int GetShapeInShapeCollectionShapeIndex(int index) => ShapeInShapeCollectionShapeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ShapeInShapeCollectionShapeCollectionIndex { get; } + public int[] ShapeInShapeCollectionShapeCollectionIndex { get; } public int GetShapeInShapeCollectionShapeCollectionIndex(int index) => ShapeInShapeCollectionShapeCollectionIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumShapeInShapeCollection => ShapeInShapeCollectionEntityTable?.NumRows ?? 0; - public IArray ShapeInShapeCollectionList { get; } + public ShapeInShapeCollection[] ShapeInShapeCollectionList { get; } public ShapeInShapeCollection GetShapeInShapeCollection(int n) { if (n < 0) return null; @@ -3210,14 +3208,14 @@ public ShapeInShapeCollection GetShapeInShapeCollection(int n) public EntityTable SystemEntityTable { get; } - public IArray SystemSystemType { get; } + public Int32[] SystemSystemType { get; } public Int32 GetSystemSystemType(int index, Int32 defaultValue = default) => SystemSystemType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray SystemFamilyTypeIndex { get; } + public int[] SystemFamilyTypeIndex { get; } public int GetSystemFamilyTypeIndex(int index) => SystemFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray SystemElementIndex { get; } + public int[] SystemElementIndex { get; } public int GetSystemElementIndex(int index) => SystemElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumSystem => SystemEntityTable?.NumRows ?? 0; - public IArray SystemList { get; } + public System[] SystemList { get; } public System GetSystem(int n) { if (n < 0) return null; @@ -3235,14 +3233,14 @@ public System GetSystem(int n) public EntityTable ElementInSystemEntityTable { get; } - public IArray ElementInSystemRoles { get; } + public Int32[] ElementInSystemRoles { get; } public Int32 GetElementInSystemRoles(int index, Int32 defaultValue = default) => ElementInSystemRoles?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementInSystemSystemIndex { get; } + public int[] ElementInSystemSystemIndex { get; } public int GetElementInSystemSystemIndex(int index) => ElementInSystemSystemIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementInSystemElementIndex { get; } + public int[] ElementInSystemElementIndex { get; } public int GetElementInSystemElementIndex(int index) => ElementInSystemElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumElementInSystem => ElementInSystemEntityTable?.NumRows ?? 0; - public IArray ElementInSystemList { get; } + public ElementInSystem[] ElementInSystemList { get; } public ElementInSystem GetElementInSystem(int n) { if (n < 0) return null; @@ -3260,16 +3258,16 @@ public ElementInSystem GetElementInSystem(int n) public EntityTable WarningEntityTable { get; } - public IArray WarningGuid { get; } + public String[] WarningGuid { get; } public String GetWarningGuid(int index, String defaultValue = "") => WarningGuid?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WarningSeverity { get; } + public String[] WarningSeverity { get; } public String GetWarningSeverity(int index, String defaultValue = "") => WarningSeverity?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WarningDescription { get; } + public String[] WarningDescription { get; } public String GetWarningDescription(int index, String defaultValue = "") => WarningDescription?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WarningBimDocumentIndex { get; } + public int[] WarningBimDocumentIndex { get; } public int GetWarningBimDocumentIndex(int index) => WarningBimDocumentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumWarning => WarningEntityTable?.NumRows ?? 0; - public IArray WarningList { get; } + public Warning[] WarningList { get; } public Warning GetWarning(int n) { if (n < 0) return null; @@ -3288,12 +3286,12 @@ public Warning GetWarning(int n) public EntityTable ElementInWarningEntityTable { get; } - public IArray ElementInWarningWarningIndex { get; } + public int[] ElementInWarningWarningIndex { get; } public int GetElementInWarningWarningIndex(int index) => ElementInWarningWarningIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementInWarningElementIndex { get; } + public int[] ElementInWarningElementIndex { get; } public int GetElementInWarningElementIndex(int index) => ElementInWarningElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumElementInWarning => ElementInWarningEntityTable?.NumRows ?? 0; - public IArray ElementInWarningList { get; } + public ElementInWarning[] ElementInWarningList { get; } public ElementInWarning GetElementInWarning(int n) { if (n < 0) return null; @@ -3310,24 +3308,24 @@ public ElementInWarning GetElementInWarning(int n) public EntityTable BasePointEntityTable { get; } - public IArray BasePointIsSurveyPoint { get; } + public Boolean[] BasePointIsSurveyPoint { get; } public Boolean GetBasePointIsSurveyPoint(int index, Boolean defaultValue = default) => BasePointIsSurveyPoint?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BasePointPosition_X { get; } + public Double[] BasePointPosition_X { get; } public Double GetBasePointPosition_X(int index, Double defaultValue = default) => BasePointPosition_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BasePointPosition_Y { get; } + public Double[] BasePointPosition_Y { get; } public Double GetBasePointPosition_Y(int index, Double defaultValue = default) => BasePointPosition_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BasePointPosition_Z { get; } + public Double[] BasePointPosition_Z { get; } public Double GetBasePointPosition_Z(int index, Double defaultValue = default) => BasePointPosition_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BasePointSharedPosition_X { get; } + public Double[] BasePointSharedPosition_X { get; } public Double GetBasePointSharedPosition_X(int index, Double defaultValue = default) => BasePointSharedPosition_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BasePointSharedPosition_Y { get; } + public Double[] BasePointSharedPosition_Y { get; } public Double GetBasePointSharedPosition_Y(int index, Double defaultValue = default) => BasePointSharedPosition_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BasePointSharedPosition_Z { get; } + public Double[] BasePointSharedPosition_Z { get; } public Double GetBasePointSharedPosition_Z(int index, Double defaultValue = default) => BasePointSharedPosition_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BasePointElementIndex { get; } + public int[] BasePointElementIndex { get; } public int GetBasePointElementIndex(int index) => BasePointElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumBasePoint => BasePointEntityTable?.NumRows ?? 0; - public IArray BasePointList { get; } + public BasePoint[] BasePointList { get; } public BasePoint GetBasePoint(int n) { if (n < 0) return null; @@ -3350,18 +3348,18 @@ public BasePoint GetBasePoint(int n) public EntityTable PhaseFilterEntityTable { get; } - public IArray PhaseFilterNew { get; } + public Int32[] PhaseFilterNew { get; } public Int32 GetPhaseFilterNew(int index, Int32 defaultValue = default) => PhaseFilterNew?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray PhaseFilterExisting { get; } + public Int32[] PhaseFilterExisting { get; } public Int32 GetPhaseFilterExisting(int index, Int32 defaultValue = default) => PhaseFilterExisting?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray PhaseFilterDemolished { get; } + public Int32[] PhaseFilterDemolished { get; } public Int32 GetPhaseFilterDemolished(int index, Int32 defaultValue = default) => PhaseFilterDemolished?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray PhaseFilterTemporary { get; } + public Int32[] PhaseFilterTemporary { get; } public Int32 GetPhaseFilterTemporary(int index, Int32 defaultValue = default) => PhaseFilterTemporary?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray PhaseFilterElementIndex { get; } + public int[] PhaseFilterElementIndex { get; } public int GetPhaseFilterElementIndex(int index) => PhaseFilterElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumPhaseFilter => PhaseFilterEntityTable?.NumRows ?? 0; - public IArray PhaseFilterList { get; } + public PhaseFilter[] PhaseFilterList { get; } public PhaseFilter GetPhaseFilter(int n) { if (n < 0) return null; @@ -3381,38 +3379,38 @@ public PhaseFilter GetPhaseFilter(int n) public EntityTable GridEntityTable { get; } - public IArray GridStartPoint_X { get; } + public Double[] GridStartPoint_X { get; } public Double GetGridStartPoint_X(int index, Double defaultValue = default) => GridStartPoint_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridStartPoint_Y { get; } + public Double[] GridStartPoint_Y { get; } public Double GetGridStartPoint_Y(int index, Double defaultValue = default) => GridStartPoint_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridStartPoint_Z { get; } + public Double[] GridStartPoint_Z { get; } public Double GetGridStartPoint_Z(int index, Double defaultValue = default) => GridStartPoint_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridEndPoint_X { get; } + public Double[] GridEndPoint_X { get; } public Double GetGridEndPoint_X(int index, Double defaultValue = default) => GridEndPoint_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridEndPoint_Y { get; } + public Double[] GridEndPoint_Y { get; } public Double GetGridEndPoint_Y(int index, Double defaultValue = default) => GridEndPoint_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridEndPoint_Z { get; } + public Double[] GridEndPoint_Z { get; } public Double GetGridEndPoint_Z(int index, Double defaultValue = default) => GridEndPoint_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridIsCurved { get; } + public Boolean[] GridIsCurved { get; } public Boolean GetGridIsCurved(int index, Boolean defaultValue = default) => GridIsCurved?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridExtents_Min_X { get; } + public Double[] GridExtents_Min_X { get; } public Double GetGridExtents_Min_X(int index, Double defaultValue = default) => GridExtents_Min_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridExtents_Min_Y { get; } + public Double[] GridExtents_Min_Y { get; } public Double GetGridExtents_Min_Y(int index, Double defaultValue = default) => GridExtents_Min_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridExtents_Min_Z { get; } + public Double[] GridExtents_Min_Z { get; } public Double GetGridExtents_Min_Z(int index, Double defaultValue = default) => GridExtents_Min_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridExtents_Max_X { get; } + public Double[] GridExtents_Max_X { get; } public Double GetGridExtents_Max_X(int index, Double defaultValue = default) => GridExtents_Max_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridExtents_Max_Y { get; } + public Double[] GridExtents_Max_Y { get; } public Double GetGridExtents_Max_Y(int index, Double defaultValue = default) => GridExtents_Max_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridExtents_Max_Z { get; } + public Double[] GridExtents_Max_Z { get; } public Double GetGridExtents_Max_Z(int index, Double defaultValue = default) => GridExtents_Max_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridFamilyTypeIndex { get; } + public int[] GridFamilyTypeIndex { get; } public int GetGridFamilyTypeIndex(int index) => GridFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray GridElementIndex { get; } + public int[] GridElementIndex { get; } public int GetGridElementIndex(int index) => GridElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumGrid => GridEntityTable?.NumRows ?? 0; - public IArray GridList { get; } + public Grid[] GridList { get; } public Grid GetGrid(int n) { if (n < 0) return null; @@ -3442,20 +3440,20 @@ public Grid GetGrid(int n) public EntityTable AreaEntityTable { get; } - public IArray AreaValue { get; } + public Double[] AreaValue { get; } public Double GetAreaValue(int index, Double defaultValue = default) => AreaValue?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AreaPerimeter { get; } + public Double[] AreaPerimeter { get; } public Double GetAreaPerimeter(int index, Double defaultValue = default) => AreaPerimeter?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AreaNumber { get; } + public String[] AreaNumber { get; } public String GetAreaNumber(int index, String defaultValue = "") => AreaNumber?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AreaIsGrossInterior { get; } + public Boolean[] AreaIsGrossInterior { get; } public Boolean GetAreaIsGrossInterior(int index, Boolean defaultValue = default) => AreaIsGrossInterior?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AreaAreaSchemeIndex { get; } + public int[] AreaAreaSchemeIndex { get; } public int GetAreaAreaSchemeIndex(int index) => AreaAreaSchemeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray AreaElementIndex { get; } + public int[] AreaElementIndex { get; } public int GetAreaElementIndex(int index) => AreaElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumArea => AreaEntityTable?.NumRows ?? 0; - public IArray AreaList { get; } + public Area[] AreaList { get; } public Area GetArea(int n) { if (n < 0) return null; @@ -3476,12 +3474,12 @@ public Area GetArea(int n) public EntityTable AreaSchemeEntityTable { get; } - public IArray AreaSchemeIsGrossBuildingArea { get; } + public Boolean[] AreaSchemeIsGrossBuildingArea { get; } public Boolean GetAreaSchemeIsGrossBuildingArea(int index, Boolean defaultValue = default) => AreaSchemeIsGrossBuildingArea?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AreaSchemeElementIndex { get; } + public int[] AreaSchemeElementIndex { get; } public int GetAreaSchemeElementIndex(int index) => AreaSchemeElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumAreaScheme => AreaSchemeEntityTable?.NumRows ?? 0; - public IArray AreaSchemeList { get; } + public AreaScheme[] AreaSchemeList { get; } public AreaScheme GetAreaScheme(int n) { if (n < 0) return null; @@ -3498,10 +3496,10 @@ public AreaScheme GetAreaScheme(int n) public EntityTable ScheduleEntityTable { get; } - public IArray ScheduleElementIndex { get; } + public int[] ScheduleElementIndex { get; } public int GetScheduleElementIndex(int index) => ScheduleElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumSchedule => ScheduleEntityTable?.NumRows ?? 0; - public IArray ScheduleList { get; } + public Schedule[] ScheduleList { get; } public Schedule GetSchedule(int n) { if (n < 0) return null; @@ -3517,14 +3515,14 @@ public Schedule GetSchedule(int n) public EntityTable ScheduleColumnEntityTable { get; } - public IArray ScheduleColumnName { get; } + public String[] ScheduleColumnName { get; } public String GetScheduleColumnName(int index, String defaultValue = "") => ScheduleColumnName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ScheduleColumnColumnIndex { get; } + public Int32[] ScheduleColumnColumnIndex { get; } public Int32 GetScheduleColumnColumnIndex(int index, Int32 defaultValue = default) => ScheduleColumnColumnIndex?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ScheduleColumnScheduleIndex { get; } + public int[] ScheduleColumnScheduleIndex { get; } public int GetScheduleColumnScheduleIndex(int index) => ScheduleColumnScheduleIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumScheduleColumn => ScheduleColumnEntityTable?.NumRows ?? 0; - public IArray ScheduleColumnList { get; } + public ScheduleColumn[] ScheduleColumnList { get; } public ScheduleColumn GetScheduleColumn(int n) { if (n < 0) return null; @@ -3542,14 +3540,14 @@ public ScheduleColumn GetScheduleColumn(int n) public EntityTable ScheduleCellEntityTable { get; } - public IArray ScheduleCellValue { get; } + public String[] ScheduleCellValue { get; } public String GetScheduleCellValue(int index, String defaultValue = "") => ScheduleCellValue?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ScheduleCellRowIndex { get; } + public Int32[] ScheduleCellRowIndex { get; } public Int32 GetScheduleCellRowIndex(int index, Int32 defaultValue = default) => ScheduleCellRowIndex?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ScheduleCellScheduleColumnIndex { get; } + public int[] ScheduleCellScheduleColumnIndex { get; } public int GetScheduleCellScheduleColumnIndex(int index) => ScheduleCellScheduleColumnIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumScheduleCell => ScheduleCellEntityTable?.NumRows ?? 0; - public IArray ScheduleCellList { get; } + public ScheduleCell[] ScheduleCellList { get; } public ScheduleCell GetScheduleCell(int n) { if (n < 0) return null; @@ -3567,10 +3565,10 @@ public ScheduleCell GetScheduleCell(int n) public EntityTable ViewSheetSetEntityTable { get; } - public IArray ViewSheetSetElementIndex { get; } + public int[] ViewSheetSetElementIndex { get; } public int GetViewSheetSetElementIndex(int index) => ViewSheetSetElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumViewSheetSet => ViewSheetSetEntityTable?.NumRows ?? 0; - public IArray ViewSheetSetList { get; } + public ViewSheetSet[] ViewSheetSetList { get; } public ViewSheetSet GetViewSheetSet(int n) { if (n < 0) return null; @@ -3586,12 +3584,12 @@ public ViewSheetSet GetViewSheetSet(int n) public EntityTable ViewSheetEntityTable { get; } - public IArray ViewSheetFamilyTypeIndex { get; } + public int[] ViewSheetFamilyTypeIndex { get; } public int GetViewSheetFamilyTypeIndex(int index) => ViewSheetFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ViewSheetElementIndex { get; } + public int[] ViewSheetElementIndex { get; } public int GetViewSheetElementIndex(int index) => ViewSheetElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumViewSheet => ViewSheetEntityTable?.NumRows ?? 0; - public IArray ViewSheetList { get; } + public ViewSheet[] ViewSheetList { get; } public ViewSheet GetViewSheet(int n) { if (n < 0) return null; @@ -3608,12 +3606,12 @@ public ViewSheet GetViewSheet(int n) public EntityTable ViewSheetInViewSheetSetEntityTable { get; } - public IArray ViewSheetInViewSheetSetViewSheetIndex { get; } + public int[] ViewSheetInViewSheetSetViewSheetIndex { get; } public int GetViewSheetInViewSheetSetViewSheetIndex(int index) => ViewSheetInViewSheetSetViewSheetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ViewSheetInViewSheetSetViewSheetSetIndex { get; } + public int[] ViewSheetInViewSheetSetViewSheetSetIndex { get; } public int GetViewSheetInViewSheetSetViewSheetSetIndex(int index) => ViewSheetInViewSheetSetViewSheetSetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumViewSheetInViewSheetSet => ViewSheetInViewSheetSetEntityTable?.NumRows ?? 0; - public IArray ViewSheetInViewSheetSetList { get; } + public ViewSheetInViewSheetSet[] ViewSheetInViewSheetSetList { get; } public ViewSheetInViewSheetSet GetViewSheetInViewSheetSet(int n) { if (n < 0) return null; @@ -3630,12 +3628,12 @@ public ViewSheetInViewSheetSet GetViewSheetInViewSheetSet(int n) public EntityTable ViewInViewSheetSetEntityTable { get; } - public IArray ViewInViewSheetSetViewIndex { get; } + public int[] ViewInViewSheetSetViewIndex { get; } public int GetViewInViewSheetSetViewIndex(int index) => ViewInViewSheetSetViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ViewInViewSheetSetViewSheetSetIndex { get; } + public int[] ViewInViewSheetSetViewSheetSetIndex { get; } public int GetViewInViewSheetSetViewSheetSetIndex(int index) => ViewInViewSheetSetViewSheetSetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumViewInViewSheetSet => ViewInViewSheetSetEntityTable?.NumRows ?? 0; - public IArray ViewInViewSheetSetList { get; } + public ViewInViewSheetSet[] ViewInViewSheetSetList { get; } public ViewInViewSheetSet GetViewInViewSheetSet(int n) { if (n < 0) return null; @@ -3652,12 +3650,12 @@ public ViewInViewSheetSet GetViewInViewSheetSet(int n) public EntityTable ViewInViewSheetEntityTable { get; } - public IArray ViewInViewSheetViewIndex { get; } + public int[] ViewInViewSheetViewIndex { get; } public int GetViewInViewSheetViewIndex(int index) => ViewInViewSheetViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ViewInViewSheetViewSheetIndex { get; } + public int[] ViewInViewSheetViewSheetIndex { get; } public int GetViewInViewSheetViewSheetIndex(int index) => ViewInViewSheetViewSheetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumViewInViewSheet => ViewInViewSheetEntityTable?.NumRows ?? 0; - public IArray ViewInViewSheetList { get; } + public ViewInViewSheet[] ViewInViewSheetList { get; } public ViewInViewSheet GetViewInViewSheet(int n) { if (n < 0) return null; @@ -3674,20 +3672,20 @@ public ViewInViewSheet GetViewInViewSheet(int n) public EntityTable SiteEntityTable { get; } - public IArray SiteLatitude { get; } + public Double[] SiteLatitude { get; } public Double GetSiteLatitude(int index, Double defaultValue = default) => SiteLatitude?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray SiteLongitude { get; } + public Double[] SiteLongitude { get; } public Double GetSiteLongitude(int index, Double defaultValue = default) => SiteLongitude?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray SiteAddress { get; } + public String[] SiteAddress { get; } public String GetSiteAddress(int index, String defaultValue = "") => SiteAddress?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray SiteElevation { get; } + public Double[] SiteElevation { get; } public Double GetSiteElevation(int index, Double defaultValue = default) => SiteElevation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray SiteNumber { get; } + public String[] SiteNumber { get; } public String GetSiteNumber(int index, String defaultValue = "") => SiteNumber?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray SiteElementIndex { get; } + public int[] SiteElementIndex { get; } public int GetSiteElementIndex(int index) => SiteElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumSite => SiteEntityTable?.NumRows ?? 0; - public IArray SiteList { get; } + public Site[] SiteList { get; } public Site GetSite(int n) { if (n < 0) return null; @@ -3708,18 +3706,18 @@ public Site GetSite(int n) public EntityTable BuildingEntityTable { get; } - public IArray BuildingElevation { get; } + public Double[] BuildingElevation { get; } public Double GetBuildingElevation(int index, Double defaultValue = default) => BuildingElevation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BuildingTerrainElevation { get; } + public Double[] BuildingTerrainElevation { get; } public Double GetBuildingTerrainElevation(int index, Double defaultValue = default) => BuildingTerrainElevation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BuildingAddress { get; } + public String[] BuildingAddress { get; } public String GetBuildingAddress(int index, String defaultValue = "") => BuildingAddress?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BuildingSiteIndex { get; } + public int[] BuildingSiteIndex { get; } public int GetBuildingSiteIndex(int index) => BuildingSiteIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray BuildingElementIndex { get; } + public int[] BuildingElementIndex { get; } public int GetBuildingElementIndex(int index) => BuildingElementIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public int NumBuilding => BuildingEntityTable?.NumRows ?? 0; - public IArray BuildingList { get; } + public Building[] BuildingList { get; } public Building GetBuilding(int n) { if (n < 0) return null; @@ -3736,60 +3734,60 @@ public Building GetBuilding(int n) // All entity collections public Dictionary> AllEntities => new Dictionary>() { - {"Vim.Asset", AssetList.ToEnumerable()}, - {"Vim.DisplayUnit", DisplayUnitList.ToEnumerable()}, - {"Vim.ParameterDescriptor", ParameterDescriptorList.ToEnumerable()}, - {"Vim.Parameter", ParameterList.ToEnumerable()}, - {"Vim.Element", ElementList.ToEnumerable()}, - {"Vim.Workset", WorksetList.ToEnumerable()}, - {"Vim.AssemblyInstance", AssemblyInstanceList.ToEnumerable()}, - {"Vim.Group", GroupList.ToEnumerable()}, - {"Vim.DesignOption", DesignOptionList.ToEnumerable()}, - {"Vim.Level", LevelList.ToEnumerable()}, - {"Vim.Phase", PhaseList.ToEnumerable()}, - {"Vim.Room", RoomList.ToEnumerable()}, - {"Vim.BimDocument", BimDocumentList.ToEnumerable()}, - {"Vim.DisplayUnitInBimDocument", DisplayUnitInBimDocumentList.ToEnumerable()}, - {"Vim.PhaseOrderInBimDocument", PhaseOrderInBimDocumentList.ToEnumerable()}, - {"Vim.Category", CategoryList.ToEnumerable()}, - {"Vim.Family", FamilyList.ToEnumerable()}, - {"Vim.FamilyType", FamilyTypeList.ToEnumerable()}, - {"Vim.FamilyInstance", FamilyInstanceList.ToEnumerable()}, - {"Vim.View", ViewList.ToEnumerable()}, - {"Vim.ElementInView", ElementInViewList.ToEnumerable()}, - {"Vim.ShapeInView", ShapeInViewList.ToEnumerable()}, - {"Vim.AssetInView", AssetInViewList.ToEnumerable()}, - {"Vim.AssetInViewSheet", AssetInViewSheetList.ToEnumerable()}, - {"Vim.LevelInView", LevelInViewList.ToEnumerable()}, - {"Vim.Camera", CameraList.ToEnumerable()}, - {"Vim.Material", MaterialList.ToEnumerable()}, - {"Vim.MaterialInElement", MaterialInElementList.ToEnumerable()}, - {"Vim.CompoundStructureLayer", CompoundStructureLayerList.ToEnumerable()}, - {"Vim.CompoundStructure", CompoundStructureList.ToEnumerable()}, - {"Vim.Node", NodeList.ToEnumerable()}, - {"Vim.Geometry", GeometryList.ToEnumerable()}, - {"Vim.Shape", ShapeList.ToEnumerable()}, - {"Vim.ShapeCollection", ShapeCollectionList.ToEnumerable()}, - {"Vim.ShapeInShapeCollection", ShapeInShapeCollectionList.ToEnumerable()}, - {"Vim.System", SystemList.ToEnumerable()}, - {"Vim.ElementInSystem", ElementInSystemList.ToEnumerable()}, - {"Vim.Warning", WarningList.ToEnumerable()}, - {"Vim.ElementInWarning", ElementInWarningList.ToEnumerable()}, - {"Vim.BasePoint", BasePointList.ToEnumerable()}, - {"Vim.PhaseFilter", PhaseFilterList.ToEnumerable()}, - {"Vim.Grid", GridList.ToEnumerable()}, - {"Vim.Area", AreaList.ToEnumerable()}, - {"Vim.AreaScheme", AreaSchemeList.ToEnumerable()}, - {"Vim.Schedule", ScheduleList.ToEnumerable()}, - {"Vim.ScheduleColumn", ScheduleColumnList.ToEnumerable()}, - {"Vim.ScheduleCell", ScheduleCellList.ToEnumerable()}, - {"Vim.ViewSheetSet", ViewSheetSetList.ToEnumerable()}, - {"Vim.ViewSheet", ViewSheetList.ToEnumerable()}, - {"Vim.ViewSheetInViewSheetSet", ViewSheetInViewSheetSetList.ToEnumerable()}, - {"Vim.ViewInViewSheetSet", ViewInViewSheetSetList.ToEnumerable()}, - {"Vim.ViewInViewSheet", ViewInViewSheetList.ToEnumerable()}, - {"Vim.Site", SiteList.ToEnumerable()}, - {"Vim.Building", BuildingList.ToEnumerable()}, + {"Vim.Asset", AssetList}, + {"Vim.DisplayUnit", DisplayUnitList}, + {"Vim.ParameterDescriptor", ParameterDescriptorList}, + {"Vim.Parameter", ParameterList}, + {"Vim.Element", ElementList}, + {"Vim.Workset", WorksetList}, + {"Vim.AssemblyInstance", AssemblyInstanceList}, + {"Vim.Group", GroupList}, + {"Vim.DesignOption", DesignOptionList}, + {"Vim.Level", LevelList}, + {"Vim.Phase", PhaseList}, + {"Vim.Room", RoomList}, + {"Vim.BimDocument", BimDocumentList}, + {"Vim.DisplayUnitInBimDocument", DisplayUnitInBimDocumentList}, + {"Vim.PhaseOrderInBimDocument", PhaseOrderInBimDocumentList}, + {"Vim.Category", CategoryList}, + {"Vim.Family", FamilyList}, + {"Vim.FamilyType", FamilyTypeList}, + {"Vim.FamilyInstance", FamilyInstanceList}, + {"Vim.View", ViewList}, + {"Vim.ElementInView", ElementInViewList}, + {"Vim.ShapeInView", ShapeInViewList}, + {"Vim.AssetInView", AssetInViewList}, + {"Vim.AssetInViewSheet", AssetInViewSheetList}, + {"Vim.LevelInView", LevelInViewList}, + {"Vim.Camera", CameraList}, + {"Vim.Material", MaterialList}, + {"Vim.MaterialInElement", MaterialInElementList}, + {"Vim.CompoundStructureLayer", CompoundStructureLayerList}, + {"Vim.CompoundStructure", CompoundStructureList}, + {"Vim.Node", NodeList}, + {"Vim.Geometry", GeometryList}, + {"Vim.Shape", ShapeList}, + {"Vim.ShapeCollection", ShapeCollectionList}, + {"Vim.ShapeInShapeCollection", ShapeInShapeCollectionList}, + {"Vim.System", SystemList}, + {"Vim.ElementInSystem", ElementInSystemList}, + {"Vim.Warning", WarningList}, + {"Vim.ElementInWarning", ElementInWarningList}, + {"Vim.BasePoint", BasePointList}, + {"Vim.PhaseFilter", PhaseFilterList}, + {"Vim.Grid", GridList}, + {"Vim.Area", AreaList}, + {"Vim.AreaScheme", AreaSchemeList}, + {"Vim.Schedule", ScheduleList}, + {"Vim.ScheduleColumn", ScheduleColumnList}, + {"Vim.ScheduleCell", ScheduleCellList}, + {"Vim.ViewSheetSet", ViewSheetSetList}, + {"Vim.ViewSheet", ViewSheetList}, + {"Vim.ViewSheetInViewSheetSet", ViewSheetInViewSheetSetList}, + {"Vim.ViewInViewSheetSet", ViewInViewSheetSetList}, + {"Vim.ViewInViewSheet", ViewInViewSheetList}, + {"Vim.Site", SiteList}, + {"Vim.Building", BuildingList}, }; // Entity types from table names @@ -3910,391 +3908,391 @@ public DocumentModel(Document d, bool inParallel = true) BuildingEntityTable = Document.GetTable("Vim.Building"); // Initialize entity arrays - AssetBufferName = AssetEntityTable?.GetStringColumnValues("string:BufferName") ?? Array.Empty().ToIArray(); - DisplayUnitSpec = DisplayUnitEntityTable?.GetStringColumnValues("string:Spec") ?? Array.Empty().ToIArray(); - DisplayUnitType = DisplayUnitEntityTable?.GetStringColumnValues("string:Type") ?? Array.Empty().ToIArray(); - DisplayUnitLabel = DisplayUnitEntityTable?.GetStringColumnValues("string:Label") ?? Array.Empty().ToIArray(); - ParameterDescriptorName = ParameterDescriptorEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty().ToIArray(); - ParameterDescriptorGroup = ParameterDescriptorEntityTable?.GetStringColumnValues("string:Group") ?? Array.Empty().ToIArray(); - ParameterDescriptorParameterType = ParameterDescriptorEntityTable?.GetStringColumnValues("string:ParameterType") ?? Array.Empty().ToIArray(); - ParameterDescriptorIsInstance = ParameterDescriptorEntityTable?.GetDataColumnValues("byte:IsInstance") ?? Array.Empty().ToIArray(); - ParameterDescriptorIsShared = ParameterDescriptorEntityTable?.GetDataColumnValues("byte:IsShared") ?? Array.Empty().ToIArray(); - ParameterDescriptorIsReadOnly = ParameterDescriptorEntityTable?.GetDataColumnValues("byte:IsReadOnly") ?? Array.Empty().ToIArray(); - ParameterDescriptorFlags = ParameterDescriptorEntityTable?.GetDataColumnValues("int:Flags") ?? Array.Empty().ToIArray(); - ParameterDescriptorGuid = ParameterDescriptorEntityTable?.GetStringColumnValues("string:Guid") ?? Array.Empty().ToIArray(); - ParameterDescriptorStorageType = ParameterDescriptorEntityTable?.GetDataColumnValues("int:StorageType") ?? Array.Empty().ToIArray(); - ParameterValue = ParameterEntityTable?.GetStringColumnValues("string:Value") ?? Array.Empty().ToIArray(); - ElementId = (ElementEntityTable?.GetDataColumnValues("long:Id") ?? ElementEntityTable?.GetDataColumnValues("int:Id")?.Select(v => (Int64) v)) ?? Array.Empty().ToIArray(); - ElementType = ElementEntityTable?.GetStringColumnValues("string:Type") ?? Array.Empty().ToIArray(); - ElementName = ElementEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty().ToIArray(); - ElementUniqueId = ElementEntityTable?.GetStringColumnValues("string:UniqueId") ?? Array.Empty().ToIArray(); - ElementLocation_X = ElementEntityTable?.GetDataColumnValues("float:Location.X") ?? Array.Empty().ToIArray(); - ElementLocation_Y = ElementEntityTable?.GetDataColumnValues("float:Location.Y") ?? Array.Empty().ToIArray(); - ElementLocation_Z = ElementEntityTable?.GetDataColumnValues("float:Location.Z") ?? Array.Empty().ToIArray(); - ElementFamilyName = ElementEntityTable?.GetStringColumnValues("string:FamilyName") ?? Array.Empty().ToIArray(); - ElementIsPinned = ElementEntityTable?.GetDataColumnValues("byte:IsPinned") ?? Array.Empty().ToIArray(); - WorksetId = WorksetEntityTable?.GetDataColumnValues("int:Id") ?? Array.Empty().ToIArray(); - WorksetName = WorksetEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty().ToIArray(); - WorksetKind = WorksetEntityTable?.GetStringColumnValues("string:Kind") ?? Array.Empty().ToIArray(); - WorksetIsOpen = WorksetEntityTable?.GetDataColumnValues("byte:IsOpen") ?? Array.Empty().ToIArray(); - WorksetIsEditable = WorksetEntityTable?.GetDataColumnValues("byte:IsEditable") ?? Array.Empty().ToIArray(); - WorksetOwner = WorksetEntityTable?.GetStringColumnValues("string:Owner") ?? Array.Empty().ToIArray(); - WorksetUniqueId = WorksetEntityTable?.GetStringColumnValues("string:UniqueId") ?? Array.Empty().ToIArray(); - AssemblyInstanceAssemblyTypeName = AssemblyInstanceEntityTable?.GetStringColumnValues("string:AssemblyTypeName") ?? Array.Empty().ToIArray(); - AssemblyInstancePosition_X = AssemblyInstanceEntityTable?.GetDataColumnValues("float:Position.X") ?? Array.Empty().ToIArray(); - AssemblyInstancePosition_Y = AssemblyInstanceEntityTable?.GetDataColumnValues("float:Position.Y") ?? Array.Empty().ToIArray(); - AssemblyInstancePosition_Z = AssemblyInstanceEntityTable?.GetDataColumnValues("float:Position.Z") ?? Array.Empty().ToIArray(); - GroupGroupType = GroupEntityTable?.GetStringColumnValues("string:GroupType") ?? Array.Empty().ToIArray(); - GroupPosition_X = GroupEntityTable?.GetDataColumnValues("float:Position.X") ?? Array.Empty().ToIArray(); - GroupPosition_Y = GroupEntityTable?.GetDataColumnValues("float:Position.Y") ?? Array.Empty().ToIArray(); - GroupPosition_Z = GroupEntityTable?.GetDataColumnValues("float:Position.Z") ?? Array.Empty().ToIArray(); - DesignOptionIsPrimary = DesignOptionEntityTable?.GetDataColumnValues("byte:IsPrimary") ?? Array.Empty().ToIArray(); - LevelElevation = LevelEntityTable?.GetDataColumnValues("double:Elevation") ?? Array.Empty().ToIArray(); - RoomBaseOffset = RoomEntityTable?.GetDataColumnValues("double:BaseOffset") ?? Array.Empty().ToIArray(); - RoomLimitOffset = RoomEntityTable?.GetDataColumnValues("double:LimitOffset") ?? Array.Empty().ToIArray(); - RoomUnboundedHeight = RoomEntityTable?.GetDataColumnValues("double:UnboundedHeight") ?? Array.Empty().ToIArray(); - RoomVolume = RoomEntityTable?.GetDataColumnValues("double:Volume") ?? Array.Empty().ToIArray(); - RoomPerimeter = RoomEntityTable?.GetDataColumnValues("double:Perimeter") ?? Array.Empty().ToIArray(); - RoomArea = RoomEntityTable?.GetDataColumnValues("double:Area") ?? Array.Empty().ToIArray(); - RoomNumber = RoomEntityTable?.GetStringColumnValues("string:Number") ?? Array.Empty().ToIArray(); - BimDocumentTitle = BimDocumentEntityTable?.GetStringColumnValues("string:Title") ?? Array.Empty().ToIArray(); - BimDocumentIsMetric = BimDocumentEntityTable?.GetDataColumnValues("byte:IsMetric") ?? Array.Empty().ToIArray(); - BimDocumentGuid = BimDocumentEntityTable?.GetStringColumnValues("string:Guid") ?? Array.Empty().ToIArray(); - BimDocumentNumSaves = BimDocumentEntityTable?.GetDataColumnValues("int:NumSaves") ?? Array.Empty().ToIArray(); - BimDocumentIsLinked = BimDocumentEntityTable?.GetDataColumnValues("byte:IsLinked") ?? Array.Empty().ToIArray(); - BimDocumentIsDetached = BimDocumentEntityTable?.GetDataColumnValues("byte:IsDetached") ?? Array.Empty().ToIArray(); - BimDocumentIsWorkshared = BimDocumentEntityTable?.GetDataColumnValues("byte:IsWorkshared") ?? Array.Empty().ToIArray(); - BimDocumentPathName = BimDocumentEntityTable?.GetStringColumnValues("string:PathName") ?? Array.Empty().ToIArray(); - BimDocumentLatitude = BimDocumentEntityTable?.GetDataColumnValues("double:Latitude") ?? Array.Empty().ToIArray(); - BimDocumentLongitude = BimDocumentEntityTable?.GetDataColumnValues("double:Longitude") ?? Array.Empty().ToIArray(); - BimDocumentTimeZone = BimDocumentEntityTable?.GetDataColumnValues("double:TimeZone") ?? Array.Empty().ToIArray(); - BimDocumentPlaceName = BimDocumentEntityTable?.GetStringColumnValues("string:PlaceName") ?? Array.Empty().ToIArray(); - BimDocumentWeatherStationName = BimDocumentEntityTable?.GetStringColumnValues("string:WeatherStationName") ?? Array.Empty().ToIArray(); - BimDocumentElevation = BimDocumentEntityTable?.GetDataColumnValues("double:Elevation") ?? Array.Empty().ToIArray(); - BimDocumentProjectLocation = BimDocumentEntityTable?.GetStringColumnValues("string:ProjectLocation") ?? Array.Empty().ToIArray(); - BimDocumentIssueDate = BimDocumentEntityTable?.GetStringColumnValues("string:IssueDate") ?? Array.Empty().ToIArray(); - BimDocumentStatus = BimDocumentEntityTable?.GetStringColumnValues("string:Status") ?? Array.Empty().ToIArray(); - BimDocumentClientName = BimDocumentEntityTable?.GetStringColumnValues("string:ClientName") ?? Array.Empty().ToIArray(); - BimDocumentAddress = BimDocumentEntityTable?.GetStringColumnValues("string:Address") ?? Array.Empty().ToIArray(); - BimDocumentName = BimDocumentEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty().ToIArray(); - BimDocumentNumber = BimDocumentEntityTable?.GetStringColumnValues("string:Number") ?? Array.Empty().ToIArray(); - BimDocumentAuthor = BimDocumentEntityTable?.GetStringColumnValues("string:Author") ?? Array.Empty().ToIArray(); - BimDocumentBuildingName = BimDocumentEntityTable?.GetStringColumnValues("string:BuildingName") ?? Array.Empty().ToIArray(); - BimDocumentOrganizationName = BimDocumentEntityTable?.GetStringColumnValues("string:OrganizationName") ?? Array.Empty().ToIArray(); - BimDocumentOrganizationDescription = BimDocumentEntityTable?.GetStringColumnValues("string:OrganizationDescription") ?? Array.Empty().ToIArray(); - BimDocumentProduct = BimDocumentEntityTable?.GetStringColumnValues("string:Product") ?? Array.Empty().ToIArray(); - BimDocumentVersion = BimDocumentEntityTable?.GetStringColumnValues("string:Version") ?? Array.Empty().ToIArray(); - BimDocumentUser = BimDocumentEntityTable?.GetStringColumnValues("string:User") ?? Array.Empty().ToIArray(); - PhaseOrderInBimDocumentOrderIndex = PhaseOrderInBimDocumentEntityTable?.GetDataColumnValues("int:OrderIndex") ?? Array.Empty().ToIArray(); - CategoryName = CategoryEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty().ToIArray(); - CategoryId = (CategoryEntityTable?.GetDataColumnValues("long:Id") ?? CategoryEntityTable?.GetDataColumnValues("int:Id")?.Select(v => (Int64) v)) ?? Array.Empty().ToIArray(); - CategoryCategoryType = CategoryEntityTable?.GetStringColumnValues("string:CategoryType") ?? Array.Empty().ToIArray(); - CategoryLineColor_X = CategoryEntityTable?.GetDataColumnValues("double:LineColor.X") ?? Array.Empty().ToIArray(); - CategoryLineColor_Y = CategoryEntityTable?.GetDataColumnValues("double:LineColor.Y") ?? Array.Empty().ToIArray(); - CategoryLineColor_Z = CategoryEntityTable?.GetDataColumnValues("double:LineColor.Z") ?? Array.Empty().ToIArray(); - CategoryBuiltInCategory = CategoryEntityTable?.GetStringColumnValues("string:BuiltInCategory") ?? Array.Empty().ToIArray(); - FamilyStructuralMaterialType = FamilyEntityTable?.GetStringColumnValues("string:StructuralMaterialType") ?? Array.Empty().ToIArray(); - FamilyStructuralSectionShape = FamilyEntityTable?.GetStringColumnValues("string:StructuralSectionShape") ?? Array.Empty().ToIArray(); - FamilyIsSystemFamily = FamilyEntityTable?.GetDataColumnValues("byte:IsSystemFamily") ?? Array.Empty().ToIArray(); - FamilyIsInPlace = FamilyEntityTable?.GetDataColumnValues("byte:IsInPlace") ?? Array.Empty().ToIArray(); - FamilyTypeIsSystemFamilyType = FamilyTypeEntityTable?.GetDataColumnValues("byte:IsSystemFamilyType") ?? Array.Empty().ToIArray(); - FamilyInstanceFacingFlipped = FamilyInstanceEntityTable?.GetDataColumnValues("byte:FacingFlipped") ?? Array.Empty().ToIArray(); - FamilyInstanceFacingOrientation_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:FacingOrientation.X") ?? Array.Empty().ToIArray(); - FamilyInstanceFacingOrientation_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:FacingOrientation.Y") ?? Array.Empty().ToIArray(); - FamilyInstanceFacingOrientation_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:FacingOrientation.Z") ?? Array.Empty().ToIArray(); - FamilyInstanceHandFlipped = FamilyInstanceEntityTable?.GetDataColumnValues("byte:HandFlipped") ?? Array.Empty().ToIArray(); - FamilyInstanceMirrored = FamilyInstanceEntityTable?.GetDataColumnValues("byte:Mirrored") ?? Array.Empty().ToIArray(); - FamilyInstanceHasModifiedGeometry = FamilyInstanceEntityTable?.GetDataColumnValues("byte:HasModifiedGeometry") ?? Array.Empty().ToIArray(); - FamilyInstanceScale = FamilyInstanceEntityTable?.GetDataColumnValues("float:Scale") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisX_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisX.X") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisX_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisX.Y") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisX_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisX.Z") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisY_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisY.X") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisY_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisY.Y") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisY_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisY.Z") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisZ_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisZ.X") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisZ_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisZ.Y") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisZ_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisZ.Z") ?? Array.Empty().ToIArray(); - FamilyInstanceTranslation_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:Translation.X") ?? Array.Empty().ToIArray(); - FamilyInstanceTranslation_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:Translation.Y") ?? Array.Empty().ToIArray(); - FamilyInstanceTranslation_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:Translation.Z") ?? Array.Empty().ToIArray(); - FamilyInstanceHandOrientation_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:HandOrientation.X") ?? Array.Empty().ToIArray(); - FamilyInstanceHandOrientation_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:HandOrientation.Y") ?? Array.Empty().ToIArray(); - FamilyInstanceHandOrientation_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:HandOrientation.Z") ?? Array.Empty().ToIArray(); - ViewTitle = ViewEntityTable?.GetStringColumnValues("string:Title") ?? Array.Empty().ToIArray(); - ViewViewType = ViewEntityTable?.GetStringColumnValues("string:ViewType") ?? Array.Empty().ToIArray(); - ViewUp_X = ViewEntityTable?.GetDataColumnValues("double:Up.X") ?? Array.Empty().ToIArray(); - ViewUp_Y = ViewEntityTable?.GetDataColumnValues("double:Up.Y") ?? Array.Empty().ToIArray(); - ViewUp_Z = ViewEntityTable?.GetDataColumnValues("double:Up.Z") ?? Array.Empty().ToIArray(); - ViewRight_X = ViewEntityTable?.GetDataColumnValues("double:Right.X") ?? Array.Empty().ToIArray(); - ViewRight_Y = ViewEntityTable?.GetDataColumnValues("double:Right.Y") ?? Array.Empty().ToIArray(); - ViewRight_Z = ViewEntityTable?.GetDataColumnValues("double:Right.Z") ?? Array.Empty().ToIArray(); - ViewOrigin_X = ViewEntityTable?.GetDataColumnValues("double:Origin.X") ?? Array.Empty().ToIArray(); - ViewOrigin_Y = ViewEntityTable?.GetDataColumnValues("double:Origin.Y") ?? Array.Empty().ToIArray(); - ViewOrigin_Z = ViewEntityTable?.GetDataColumnValues("double:Origin.Z") ?? Array.Empty().ToIArray(); - ViewViewDirection_X = ViewEntityTable?.GetDataColumnValues("double:ViewDirection.X") ?? Array.Empty().ToIArray(); - ViewViewDirection_Y = ViewEntityTable?.GetDataColumnValues("double:ViewDirection.Y") ?? Array.Empty().ToIArray(); - ViewViewDirection_Z = ViewEntityTable?.GetDataColumnValues("double:ViewDirection.Z") ?? Array.Empty().ToIArray(); - ViewViewPosition_X = ViewEntityTable?.GetDataColumnValues("double:ViewPosition.X") ?? Array.Empty().ToIArray(); - ViewViewPosition_Y = ViewEntityTable?.GetDataColumnValues("double:ViewPosition.Y") ?? Array.Empty().ToIArray(); - ViewViewPosition_Z = ViewEntityTable?.GetDataColumnValues("double:ViewPosition.Z") ?? Array.Empty().ToIArray(); - ViewScale = ViewEntityTable?.GetDataColumnValues("double:Scale") ?? Array.Empty().ToIArray(); - ViewOutline_Min_X = ViewEntityTable?.GetDataColumnValues("double:Outline.Min.X") ?? Array.Empty().ToIArray(); - ViewOutline_Min_Y = ViewEntityTable?.GetDataColumnValues("double:Outline.Min.Y") ?? Array.Empty().ToIArray(); - ViewOutline_Max_X = ViewEntityTable?.GetDataColumnValues("double:Outline.Max.X") ?? Array.Empty().ToIArray(); - ViewOutline_Max_Y = ViewEntityTable?.GetDataColumnValues("double:Outline.Max.Y") ?? Array.Empty().ToIArray(); - ViewDetailLevel = ViewEntityTable?.GetDataColumnValues("int:DetailLevel") ?? Array.Empty().ToIArray(); - LevelInViewExtents_Min_X = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Min.X") ?? Array.Empty().ToIArray(); - LevelInViewExtents_Min_Y = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Min.Y") ?? Array.Empty().ToIArray(); - LevelInViewExtents_Min_Z = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Min.Z") ?? Array.Empty().ToIArray(); - LevelInViewExtents_Max_X = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Max.X") ?? Array.Empty().ToIArray(); - LevelInViewExtents_Max_Y = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Max.Y") ?? Array.Empty().ToIArray(); - LevelInViewExtents_Max_Z = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Max.Z") ?? Array.Empty().ToIArray(); - CameraId = CameraEntityTable?.GetDataColumnValues("int:Id") ?? Array.Empty().ToIArray(); - CameraIsPerspective = CameraEntityTable?.GetDataColumnValues("int:IsPerspective") ?? Array.Empty().ToIArray(); - CameraVerticalExtent = CameraEntityTable?.GetDataColumnValues("double:VerticalExtent") ?? Array.Empty().ToIArray(); - CameraHorizontalExtent = CameraEntityTable?.GetDataColumnValues("double:HorizontalExtent") ?? Array.Empty().ToIArray(); - CameraFarDistance = CameraEntityTable?.GetDataColumnValues("double:FarDistance") ?? Array.Empty().ToIArray(); - CameraNearDistance = CameraEntityTable?.GetDataColumnValues("double:NearDistance") ?? Array.Empty().ToIArray(); - CameraTargetDistance = CameraEntityTable?.GetDataColumnValues("double:TargetDistance") ?? Array.Empty().ToIArray(); - CameraRightOffset = CameraEntityTable?.GetDataColumnValues("double:RightOffset") ?? Array.Empty().ToIArray(); - CameraUpOffset = CameraEntityTable?.GetDataColumnValues("double:UpOffset") ?? Array.Empty().ToIArray(); - MaterialName = MaterialEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty().ToIArray(); - MaterialMaterialCategory = MaterialEntityTable?.GetStringColumnValues("string:MaterialCategory") ?? Array.Empty().ToIArray(); - MaterialColor_X = MaterialEntityTable?.GetDataColumnValues("double:Color.X") ?? Array.Empty().ToIArray(); - MaterialColor_Y = MaterialEntityTable?.GetDataColumnValues("double:Color.Y") ?? Array.Empty().ToIArray(); - MaterialColor_Z = MaterialEntityTable?.GetDataColumnValues("double:Color.Z") ?? Array.Empty().ToIArray(); - MaterialColorUvScaling_X = MaterialEntityTable?.GetDataColumnValues("double:ColorUvScaling.X") ?? Array.Empty().ToIArray(); - MaterialColorUvScaling_Y = MaterialEntityTable?.GetDataColumnValues("double:ColorUvScaling.Y") ?? Array.Empty().ToIArray(); - MaterialColorUvOffset_X = MaterialEntityTable?.GetDataColumnValues("double:ColorUvOffset.X") ?? Array.Empty().ToIArray(); - MaterialColorUvOffset_Y = MaterialEntityTable?.GetDataColumnValues("double:ColorUvOffset.Y") ?? Array.Empty().ToIArray(); - MaterialNormalUvScaling_X = MaterialEntityTable?.GetDataColumnValues("double:NormalUvScaling.X") ?? Array.Empty().ToIArray(); - MaterialNormalUvScaling_Y = MaterialEntityTable?.GetDataColumnValues("double:NormalUvScaling.Y") ?? Array.Empty().ToIArray(); - MaterialNormalUvOffset_X = MaterialEntityTable?.GetDataColumnValues("double:NormalUvOffset.X") ?? Array.Empty().ToIArray(); - MaterialNormalUvOffset_Y = MaterialEntityTable?.GetDataColumnValues("double:NormalUvOffset.Y") ?? Array.Empty().ToIArray(); - MaterialNormalAmount = MaterialEntityTable?.GetDataColumnValues("double:NormalAmount") ?? Array.Empty().ToIArray(); - MaterialGlossiness = MaterialEntityTable?.GetDataColumnValues("double:Glossiness") ?? Array.Empty().ToIArray(); - MaterialSmoothness = MaterialEntityTable?.GetDataColumnValues("double:Smoothness") ?? Array.Empty().ToIArray(); - MaterialTransparency = MaterialEntityTable?.GetDataColumnValues("double:Transparency") ?? Array.Empty().ToIArray(); - MaterialInElementArea = MaterialInElementEntityTable?.GetDataColumnValues("double:Area") ?? Array.Empty().ToIArray(); - MaterialInElementVolume = MaterialInElementEntityTable?.GetDataColumnValues("double:Volume") ?? Array.Empty().ToIArray(); - MaterialInElementIsPaint = MaterialInElementEntityTable?.GetDataColumnValues("byte:IsPaint") ?? Array.Empty().ToIArray(); - CompoundStructureLayerOrderIndex = CompoundStructureLayerEntityTable?.GetDataColumnValues("int:OrderIndex") ?? Array.Empty().ToIArray(); - CompoundStructureLayerWidth = CompoundStructureLayerEntityTable?.GetDataColumnValues("double:Width") ?? Array.Empty().ToIArray(); - CompoundStructureLayerMaterialFunctionAssignment = CompoundStructureLayerEntityTable?.GetStringColumnValues("string:MaterialFunctionAssignment") ?? Array.Empty().ToIArray(); - CompoundStructureWidth = CompoundStructureEntityTable?.GetDataColumnValues("double:Width") ?? Array.Empty().ToIArray(); - GeometryBox_Min_X = GeometryEntityTable?.GetDataColumnValues("float:Box.Min.X") ?? Array.Empty().ToIArray(); - GeometryBox_Min_Y = GeometryEntityTable?.GetDataColumnValues("float:Box.Min.Y") ?? Array.Empty().ToIArray(); - GeometryBox_Min_Z = GeometryEntityTable?.GetDataColumnValues("float:Box.Min.Z") ?? Array.Empty().ToIArray(); - GeometryBox_Max_X = GeometryEntityTable?.GetDataColumnValues("float:Box.Max.X") ?? Array.Empty().ToIArray(); - GeometryBox_Max_Y = GeometryEntityTable?.GetDataColumnValues("float:Box.Max.Y") ?? Array.Empty().ToIArray(); - GeometryBox_Max_Z = GeometryEntityTable?.GetDataColumnValues("float:Box.Max.Z") ?? Array.Empty().ToIArray(); - GeometryVertexCount = GeometryEntityTable?.GetDataColumnValues("int:VertexCount") ?? Array.Empty().ToIArray(); - GeometryFaceCount = GeometryEntityTable?.GetDataColumnValues("int:FaceCount") ?? Array.Empty().ToIArray(); - SystemSystemType = SystemEntityTable?.GetDataColumnValues("int:SystemType") ?? Array.Empty().ToIArray(); - ElementInSystemRoles = ElementInSystemEntityTable?.GetDataColumnValues("int:Roles") ?? Array.Empty().ToIArray(); - WarningGuid = WarningEntityTable?.GetStringColumnValues("string:Guid") ?? Array.Empty().ToIArray(); - WarningSeverity = WarningEntityTable?.GetStringColumnValues("string:Severity") ?? Array.Empty().ToIArray(); - WarningDescription = WarningEntityTable?.GetStringColumnValues("string:Description") ?? Array.Empty().ToIArray(); - BasePointIsSurveyPoint = BasePointEntityTable?.GetDataColumnValues("byte:IsSurveyPoint") ?? Array.Empty().ToIArray(); - BasePointPosition_X = BasePointEntityTable?.GetDataColumnValues("double:Position.X") ?? Array.Empty().ToIArray(); - BasePointPosition_Y = BasePointEntityTable?.GetDataColumnValues("double:Position.Y") ?? Array.Empty().ToIArray(); - BasePointPosition_Z = BasePointEntityTable?.GetDataColumnValues("double:Position.Z") ?? Array.Empty().ToIArray(); - BasePointSharedPosition_X = BasePointEntityTable?.GetDataColumnValues("double:SharedPosition.X") ?? Array.Empty().ToIArray(); - BasePointSharedPosition_Y = BasePointEntityTable?.GetDataColumnValues("double:SharedPosition.Y") ?? Array.Empty().ToIArray(); - BasePointSharedPosition_Z = BasePointEntityTable?.GetDataColumnValues("double:SharedPosition.Z") ?? Array.Empty().ToIArray(); - PhaseFilterNew = PhaseFilterEntityTable?.GetDataColumnValues("int:New") ?? Array.Empty().ToIArray(); - PhaseFilterExisting = PhaseFilterEntityTable?.GetDataColumnValues("int:Existing") ?? Array.Empty().ToIArray(); - PhaseFilterDemolished = PhaseFilterEntityTable?.GetDataColumnValues("int:Demolished") ?? Array.Empty().ToIArray(); - PhaseFilterTemporary = PhaseFilterEntityTable?.GetDataColumnValues("int:Temporary") ?? Array.Empty().ToIArray(); - GridStartPoint_X = GridEntityTable?.GetDataColumnValues("double:StartPoint.X") ?? Array.Empty().ToIArray(); - GridStartPoint_Y = GridEntityTable?.GetDataColumnValues("double:StartPoint.Y") ?? Array.Empty().ToIArray(); - GridStartPoint_Z = GridEntityTable?.GetDataColumnValues("double:StartPoint.Z") ?? Array.Empty().ToIArray(); - GridEndPoint_X = GridEntityTable?.GetDataColumnValues("double:EndPoint.X") ?? Array.Empty().ToIArray(); - GridEndPoint_Y = GridEntityTable?.GetDataColumnValues("double:EndPoint.Y") ?? Array.Empty().ToIArray(); - GridEndPoint_Z = GridEntityTable?.GetDataColumnValues("double:EndPoint.Z") ?? Array.Empty().ToIArray(); - GridIsCurved = GridEntityTable?.GetDataColumnValues("byte:IsCurved") ?? Array.Empty().ToIArray(); - GridExtents_Min_X = GridEntityTable?.GetDataColumnValues("double:Extents.Min.X") ?? Array.Empty().ToIArray(); - GridExtents_Min_Y = GridEntityTable?.GetDataColumnValues("double:Extents.Min.Y") ?? Array.Empty().ToIArray(); - GridExtents_Min_Z = GridEntityTable?.GetDataColumnValues("double:Extents.Min.Z") ?? Array.Empty().ToIArray(); - GridExtents_Max_X = GridEntityTable?.GetDataColumnValues("double:Extents.Max.X") ?? Array.Empty().ToIArray(); - GridExtents_Max_Y = GridEntityTable?.GetDataColumnValues("double:Extents.Max.Y") ?? Array.Empty().ToIArray(); - GridExtents_Max_Z = GridEntityTable?.GetDataColumnValues("double:Extents.Max.Z") ?? Array.Empty().ToIArray(); - AreaValue = AreaEntityTable?.GetDataColumnValues("double:Value") ?? Array.Empty().ToIArray(); - AreaPerimeter = AreaEntityTable?.GetDataColumnValues("double:Perimeter") ?? Array.Empty().ToIArray(); - AreaNumber = AreaEntityTable?.GetStringColumnValues("string:Number") ?? Array.Empty().ToIArray(); - AreaIsGrossInterior = AreaEntityTable?.GetDataColumnValues("byte:IsGrossInterior") ?? Array.Empty().ToIArray(); - AreaSchemeIsGrossBuildingArea = AreaSchemeEntityTable?.GetDataColumnValues("byte:IsGrossBuildingArea") ?? Array.Empty().ToIArray(); - ScheduleColumnName = ScheduleColumnEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty().ToIArray(); - ScheduleColumnColumnIndex = ScheduleColumnEntityTable?.GetDataColumnValues("int:ColumnIndex") ?? Array.Empty().ToIArray(); - ScheduleCellValue = ScheduleCellEntityTable?.GetStringColumnValues("string:Value") ?? Array.Empty().ToIArray(); - ScheduleCellRowIndex = ScheduleCellEntityTable?.GetDataColumnValues("int:RowIndex") ?? Array.Empty().ToIArray(); - SiteLatitude = SiteEntityTable?.GetDataColumnValues("double:Latitude") ?? Array.Empty().ToIArray(); - SiteLongitude = SiteEntityTable?.GetDataColumnValues("double:Longitude") ?? Array.Empty().ToIArray(); - SiteAddress = SiteEntityTable?.GetStringColumnValues("string:Address") ?? Array.Empty().ToIArray(); - SiteElevation = SiteEntityTable?.GetDataColumnValues("double:Elevation") ?? Array.Empty().ToIArray(); - SiteNumber = SiteEntityTable?.GetStringColumnValues("string:Number") ?? Array.Empty().ToIArray(); - BuildingElevation = BuildingEntityTable?.GetDataColumnValues("double:Elevation") ?? Array.Empty().ToIArray(); - BuildingTerrainElevation = BuildingEntityTable?.GetDataColumnValues("double:TerrainElevation") ?? Array.Empty().ToIArray(); - BuildingAddress = BuildingEntityTable?.GetStringColumnValues("string:Address") ?? Array.Empty().ToIArray(); + AssetBufferName = AssetEntityTable?.GetStringColumnValues("string:BufferName") ?? Array.Empty(); + DisplayUnitSpec = DisplayUnitEntityTable?.GetStringColumnValues("string:Spec") ?? Array.Empty(); + DisplayUnitType = DisplayUnitEntityTable?.GetStringColumnValues("string:Type") ?? Array.Empty(); + DisplayUnitLabel = DisplayUnitEntityTable?.GetStringColumnValues("string:Label") ?? Array.Empty(); + ParameterDescriptorName = ParameterDescriptorEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty(); + ParameterDescriptorGroup = ParameterDescriptorEntityTable?.GetStringColumnValues("string:Group") ?? Array.Empty(); + ParameterDescriptorParameterType = ParameterDescriptorEntityTable?.GetStringColumnValues("string:ParameterType") ?? Array.Empty(); + ParameterDescriptorIsInstance = ParameterDescriptorEntityTable?.GetDataColumnValues("byte:IsInstance") ?? Array.Empty(); + ParameterDescriptorIsShared = ParameterDescriptorEntityTable?.GetDataColumnValues("byte:IsShared") ?? Array.Empty(); + ParameterDescriptorIsReadOnly = ParameterDescriptorEntityTable?.GetDataColumnValues("byte:IsReadOnly") ?? Array.Empty(); + ParameterDescriptorFlags = ParameterDescriptorEntityTable?.GetDataColumnValues("int:Flags") ?? Array.Empty(); + ParameterDescriptorGuid = ParameterDescriptorEntityTable?.GetStringColumnValues("string:Guid") ?? Array.Empty(); + ParameterDescriptorStorageType = ParameterDescriptorEntityTable?.GetDataColumnValues("int:StorageType") ?? Array.Empty(); + ParameterValue = ParameterEntityTable?.GetStringColumnValues("string:Value") ?? Array.Empty(); + ElementId = (ElementEntityTable?.GetDataColumnValues("long:Id") ?? ElementEntityTable?.GetDataColumnValues("int:Id")?.Select(v => (Int64) v).ToArray()) ?? Array.Empty(); + ElementType = ElementEntityTable?.GetStringColumnValues("string:Type") ?? Array.Empty(); + ElementName = ElementEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty(); + ElementUniqueId = ElementEntityTable?.GetStringColumnValues("string:UniqueId") ?? Array.Empty(); + ElementLocation_X = ElementEntityTable?.GetDataColumnValues("float:Location.X") ?? Array.Empty(); + ElementLocation_Y = ElementEntityTable?.GetDataColumnValues("float:Location.Y") ?? Array.Empty(); + ElementLocation_Z = ElementEntityTable?.GetDataColumnValues("float:Location.Z") ?? Array.Empty(); + ElementFamilyName = ElementEntityTable?.GetStringColumnValues("string:FamilyName") ?? Array.Empty(); + ElementIsPinned = ElementEntityTable?.GetDataColumnValues("byte:IsPinned") ?? Array.Empty(); + WorksetId = WorksetEntityTable?.GetDataColumnValues("int:Id") ?? Array.Empty(); + WorksetName = WorksetEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty(); + WorksetKind = WorksetEntityTable?.GetStringColumnValues("string:Kind") ?? Array.Empty(); + WorksetIsOpen = WorksetEntityTable?.GetDataColumnValues("byte:IsOpen") ?? Array.Empty(); + WorksetIsEditable = WorksetEntityTable?.GetDataColumnValues("byte:IsEditable") ?? Array.Empty(); + WorksetOwner = WorksetEntityTable?.GetStringColumnValues("string:Owner") ?? Array.Empty(); + WorksetUniqueId = WorksetEntityTable?.GetStringColumnValues("string:UniqueId") ?? Array.Empty(); + AssemblyInstanceAssemblyTypeName = AssemblyInstanceEntityTable?.GetStringColumnValues("string:AssemblyTypeName") ?? Array.Empty(); + AssemblyInstancePosition_X = AssemblyInstanceEntityTable?.GetDataColumnValues("float:Position.X") ?? Array.Empty(); + AssemblyInstancePosition_Y = AssemblyInstanceEntityTable?.GetDataColumnValues("float:Position.Y") ?? Array.Empty(); + AssemblyInstancePosition_Z = AssemblyInstanceEntityTable?.GetDataColumnValues("float:Position.Z") ?? Array.Empty(); + GroupGroupType = GroupEntityTable?.GetStringColumnValues("string:GroupType") ?? Array.Empty(); + GroupPosition_X = GroupEntityTable?.GetDataColumnValues("float:Position.X") ?? Array.Empty(); + GroupPosition_Y = GroupEntityTable?.GetDataColumnValues("float:Position.Y") ?? Array.Empty(); + GroupPosition_Z = GroupEntityTable?.GetDataColumnValues("float:Position.Z") ?? Array.Empty(); + DesignOptionIsPrimary = DesignOptionEntityTable?.GetDataColumnValues("byte:IsPrimary") ?? Array.Empty(); + LevelElevation = LevelEntityTable?.GetDataColumnValues("double:Elevation") ?? Array.Empty(); + RoomBaseOffset = RoomEntityTable?.GetDataColumnValues("double:BaseOffset") ?? Array.Empty(); + RoomLimitOffset = RoomEntityTable?.GetDataColumnValues("double:LimitOffset") ?? Array.Empty(); + RoomUnboundedHeight = RoomEntityTable?.GetDataColumnValues("double:UnboundedHeight") ?? Array.Empty(); + RoomVolume = RoomEntityTable?.GetDataColumnValues("double:Volume") ?? Array.Empty(); + RoomPerimeter = RoomEntityTable?.GetDataColumnValues("double:Perimeter") ?? Array.Empty(); + RoomArea = RoomEntityTable?.GetDataColumnValues("double:Area") ?? Array.Empty(); + RoomNumber = RoomEntityTable?.GetStringColumnValues("string:Number") ?? Array.Empty(); + BimDocumentTitle = BimDocumentEntityTable?.GetStringColumnValues("string:Title") ?? Array.Empty(); + BimDocumentIsMetric = BimDocumentEntityTable?.GetDataColumnValues("byte:IsMetric") ?? Array.Empty(); + BimDocumentGuid = BimDocumentEntityTable?.GetStringColumnValues("string:Guid") ?? Array.Empty(); + BimDocumentNumSaves = BimDocumentEntityTable?.GetDataColumnValues("int:NumSaves") ?? Array.Empty(); + BimDocumentIsLinked = BimDocumentEntityTable?.GetDataColumnValues("byte:IsLinked") ?? Array.Empty(); + BimDocumentIsDetached = BimDocumentEntityTable?.GetDataColumnValues("byte:IsDetached") ?? Array.Empty(); + BimDocumentIsWorkshared = BimDocumentEntityTable?.GetDataColumnValues("byte:IsWorkshared") ?? Array.Empty(); + BimDocumentPathName = BimDocumentEntityTable?.GetStringColumnValues("string:PathName") ?? Array.Empty(); + BimDocumentLatitude = BimDocumentEntityTable?.GetDataColumnValues("double:Latitude") ?? Array.Empty(); + BimDocumentLongitude = BimDocumentEntityTable?.GetDataColumnValues("double:Longitude") ?? Array.Empty(); + BimDocumentTimeZone = BimDocumentEntityTable?.GetDataColumnValues("double:TimeZone") ?? Array.Empty(); + BimDocumentPlaceName = BimDocumentEntityTable?.GetStringColumnValues("string:PlaceName") ?? Array.Empty(); + BimDocumentWeatherStationName = BimDocumentEntityTable?.GetStringColumnValues("string:WeatherStationName") ?? Array.Empty(); + BimDocumentElevation = BimDocumentEntityTable?.GetDataColumnValues("double:Elevation") ?? Array.Empty(); + BimDocumentProjectLocation = BimDocumentEntityTable?.GetStringColumnValues("string:ProjectLocation") ?? Array.Empty(); + BimDocumentIssueDate = BimDocumentEntityTable?.GetStringColumnValues("string:IssueDate") ?? Array.Empty(); + BimDocumentStatus = BimDocumentEntityTable?.GetStringColumnValues("string:Status") ?? Array.Empty(); + BimDocumentClientName = BimDocumentEntityTable?.GetStringColumnValues("string:ClientName") ?? Array.Empty(); + BimDocumentAddress = BimDocumentEntityTable?.GetStringColumnValues("string:Address") ?? Array.Empty(); + BimDocumentName = BimDocumentEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty(); + BimDocumentNumber = BimDocumentEntityTable?.GetStringColumnValues("string:Number") ?? Array.Empty(); + BimDocumentAuthor = BimDocumentEntityTable?.GetStringColumnValues("string:Author") ?? Array.Empty(); + BimDocumentBuildingName = BimDocumentEntityTable?.GetStringColumnValues("string:BuildingName") ?? Array.Empty(); + BimDocumentOrganizationName = BimDocumentEntityTable?.GetStringColumnValues("string:OrganizationName") ?? Array.Empty(); + BimDocumentOrganizationDescription = BimDocumentEntityTable?.GetStringColumnValues("string:OrganizationDescription") ?? Array.Empty(); + BimDocumentProduct = BimDocumentEntityTable?.GetStringColumnValues("string:Product") ?? Array.Empty(); + BimDocumentVersion = BimDocumentEntityTable?.GetStringColumnValues("string:Version") ?? Array.Empty(); + BimDocumentUser = BimDocumentEntityTable?.GetStringColumnValues("string:User") ?? Array.Empty(); + PhaseOrderInBimDocumentOrderIndex = PhaseOrderInBimDocumentEntityTable?.GetDataColumnValues("int:OrderIndex") ?? Array.Empty(); + CategoryName = CategoryEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty(); + CategoryId = (CategoryEntityTable?.GetDataColumnValues("long:Id") ?? CategoryEntityTable?.GetDataColumnValues("int:Id")?.Select(v => (Int64) v).ToArray()) ?? Array.Empty(); + CategoryCategoryType = CategoryEntityTable?.GetStringColumnValues("string:CategoryType") ?? Array.Empty(); + CategoryLineColor_X = CategoryEntityTable?.GetDataColumnValues("double:LineColor.X") ?? Array.Empty(); + CategoryLineColor_Y = CategoryEntityTable?.GetDataColumnValues("double:LineColor.Y") ?? Array.Empty(); + CategoryLineColor_Z = CategoryEntityTable?.GetDataColumnValues("double:LineColor.Z") ?? Array.Empty(); + CategoryBuiltInCategory = CategoryEntityTable?.GetStringColumnValues("string:BuiltInCategory") ?? Array.Empty(); + FamilyStructuralMaterialType = FamilyEntityTable?.GetStringColumnValues("string:StructuralMaterialType") ?? Array.Empty(); + FamilyStructuralSectionShape = FamilyEntityTable?.GetStringColumnValues("string:StructuralSectionShape") ?? Array.Empty(); + FamilyIsSystemFamily = FamilyEntityTable?.GetDataColumnValues("byte:IsSystemFamily") ?? Array.Empty(); + FamilyIsInPlace = FamilyEntityTable?.GetDataColumnValues("byte:IsInPlace") ?? Array.Empty(); + FamilyTypeIsSystemFamilyType = FamilyTypeEntityTable?.GetDataColumnValues("byte:IsSystemFamilyType") ?? Array.Empty(); + FamilyInstanceFacingFlipped = FamilyInstanceEntityTable?.GetDataColumnValues("byte:FacingFlipped") ?? Array.Empty(); + FamilyInstanceFacingOrientation_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:FacingOrientation.X") ?? Array.Empty(); + FamilyInstanceFacingOrientation_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:FacingOrientation.Y") ?? Array.Empty(); + FamilyInstanceFacingOrientation_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:FacingOrientation.Z") ?? Array.Empty(); + FamilyInstanceHandFlipped = FamilyInstanceEntityTable?.GetDataColumnValues("byte:HandFlipped") ?? Array.Empty(); + FamilyInstanceMirrored = FamilyInstanceEntityTable?.GetDataColumnValues("byte:Mirrored") ?? Array.Empty(); + FamilyInstanceHasModifiedGeometry = FamilyInstanceEntityTable?.GetDataColumnValues("byte:HasModifiedGeometry") ?? Array.Empty(); + FamilyInstanceScale = FamilyInstanceEntityTable?.GetDataColumnValues("float:Scale") ?? Array.Empty(); + FamilyInstanceBasisX_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisX.X") ?? Array.Empty(); + FamilyInstanceBasisX_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisX.Y") ?? Array.Empty(); + FamilyInstanceBasisX_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisX.Z") ?? Array.Empty(); + FamilyInstanceBasisY_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisY.X") ?? Array.Empty(); + FamilyInstanceBasisY_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisY.Y") ?? Array.Empty(); + FamilyInstanceBasisY_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisY.Z") ?? Array.Empty(); + FamilyInstanceBasisZ_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisZ.X") ?? Array.Empty(); + FamilyInstanceBasisZ_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisZ.Y") ?? Array.Empty(); + FamilyInstanceBasisZ_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisZ.Z") ?? Array.Empty(); + FamilyInstanceTranslation_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:Translation.X") ?? Array.Empty(); + FamilyInstanceTranslation_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:Translation.Y") ?? Array.Empty(); + FamilyInstanceTranslation_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:Translation.Z") ?? Array.Empty(); + FamilyInstanceHandOrientation_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:HandOrientation.X") ?? Array.Empty(); + FamilyInstanceHandOrientation_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:HandOrientation.Y") ?? Array.Empty(); + FamilyInstanceHandOrientation_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:HandOrientation.Z") ?? Array.Empty(); + ViewTitle = ViewEntityTable?.GetStringColumnValues("string:Title") ?? Array.Empty(); + ViewViewType = ViewEntityTable?.GetStringColumnValues("string:ViewType") ?? Array.Empty(); + ViewUp_X = ViewEntityTable?.GetDataColumnValues("double:Up.X") ?? Array.Empty(); + ViewUp_Y = ViewEntityTable?.GetDataColumnValues("double:Up.Y") ?? Array.Empty(); + ViewUp_Z = ViewEntityTable?.GetDataColumnValues("double:Up.Z") ?? Array.Empty(); + ViewRight_X = ViewEntityTable?.GetDataColumnValues("double:Right.X") ?? Array.Empty(); + ViewRight_Y = ViewEntityTable?.GetDataColumnValues("double:Right.Y") ?? Array.Empty(); + ViewRight_Z = ViewEntityTable?.GetDataColumnValues("double:Right.Z") ?? Array.Empty(); + ViewOrigin_X = ViewEntityTable?.GetDataColumnValues("double:Origin.X") ?? Array.Empty(); + ViewOrigin_Y = ViewEntityTable?.GetDataColumnValues("double:Origin.Y") ?? Array.Empty(); + ViewOrigin_Z = ViewEntityTable?.GetDataColumnValues("double:Origin.Z") ?? Array.Empty(); + ViewViewDirection_X = ViewEntityTable?.GetDataColumnValues("double:ViewDirection.X") ?? Array.Empty(); + ViewViewDirection_Y = ViewEntityTable?.GetDataColumnValues("double:ViewDirection.Y") ?? Array.Empty(); + ViewViewDirection_Z = ViewEntityTable?.GetDataColumnValues("double:ViewDirection.Z") ?? Array.Empty(); + ViewViewPosition_X = ViewEntityTable?.GetDataColumnValues("double:ViewPosition.X") ?? Array.Empty(); + ViewViewPosition_Y = ViewEntityTable?.GetDataColumnValues("double:ViewPosition.Y") ?? Array.Empty(); + ViewViewPosition_Z = ViewEntityTable?.GetDataColumnValues("double:ViewPosition.Z") ?? Array.Empty(); + ViewScale = ViewEntityTable?.GetDataColumnValues("double:Scale") ?? Array.Empty(); + ViewOutline_Min_X = ViewEntityTable?.GetDataColumnValues("double:Outline.Min.X") ?? Array.Empty(); + ViewOutline_Min_Y = ViewEntityTable?.GetDataColumnValues("double:Outline.Min.Y") ?? Array.Empty(); + ViewOutline_Max_X = ViewEntityTable?.GetDataColumnValues("double:Outline.Max.X") ?? Array.Empty(); + ViewOutline_Max_Y = ViewEntityTable?.GetDataColumnValues("double:Outline.Max.Y") ?? Array.Empty(); + ViewDetailLevel = ViewEntityTable?.GetDataColumnValues("int:DetailLevel") ?? Array.Empty(); + LevelInViewExtents_Min_X = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Min.X") ?? Array.Empty(); + LevelInViewExtents_Min_Y = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Min.Y") ?? Array.Empty(); + LevelInViewExtents_Min_Z = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Min.Z") ?? Array.Empty(); + LevelInViewExtents_Max_X = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Max.X") ?? Array.Empty(); + LevelInViewExtents_Max_Y = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Max.Y") ?? Array.Empty(); + LevelInViewExtents_Max_Z = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Max.Z") ?? Array.Empty(); + CameraId = CameraEntityTable?.GetDataColumnValues("int:Id") ?? Array.Empty(); + CameraIsPerspective = CameraEntityTable?.GetDataColumnValues("int:IsPerspective") ?? Array.Empty(); + CameraVerticalExtent = CameraEntityTable?.GetDataColumnValues("double:VerticalExtent") ?? Array.Empty(); + CameraHorizontalExtent = CameraEntityTable?.GetDataColumnValues("double:HorizontalExtent") ?? Array.Empty(); + CameraFarDistance = CameraEntityTable?.GetDataColumnValues("double:FarDistance") ?? Array.Empty(); + CameraNearDistance = CameraEntityTable?.GetDataColumnValues("double:NearDistance") ?? Array.Empty(); + CameraTargetDistance = CameraEntityTable?.GetDataColumnValues("double:TargetDistance") ?? Array.Empty(); + CameraRightOffset = CameraEntityTable?.GetDataColumnValues("double:RightOffset") ?? Array.Empty(); + CameraUpOffset = CameraEntityTable?.GetDataColumnValues("double:UpOffset") ?? Array.Empty(); + MaterialName = MaterialEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty(); + MaterialMaterialCategory = MaterialEntityTable?.GetStringColumnValues("string:MaterialCategory") ?? Array.Empty(); + MaterialColor_X = MaterialEntityTable?.GetDataColumnValues("double:Color.X") ?? Array.Empty(); + MaterialColor_Y = MaterialEntityTable?.GetDataColumnValues("double:Color.Y") ?? Array.Empty(); + MaterialColor_Z = MaterialEntityTable?.GetDataColumnValues("double:Color.Z") ?? Array.Empty(); + MaterialColorUvScaling_X = MaterialEntityTable?.GetDataColumnValues("double:ColorUvScaling.X") ?? Array.Empty(); + MaterialColorUvScaling_Y = MaterialEntityTable?.GetDataColumnValues("double:ColorUvScaling.Y") ?? Array.Empty(); + MaterialColorUvOffset_X = MaterialEntityTable?.GetDataColumnValues("double:ColorUvOffset.X") ?? Array.Empty(); + MaterialColorUvOffset_Y = MaterialEntityTable?.GetDataColumnValues("double:ColorUvOffset.Y") ?? Array.Empty(); + MaterialNormalUvScaling_X = MaterialEntityTable?.GetDataColumnValues("double:NormalUvScaling.X") ?? Array.Empty(); + MaterialNormalUvScaling_Y = MaterialEntityTable?.GetDataColumnValues("double:NormalUvScaling.Y") ?? Array.Empty(); + MaterialNormalUvOffset_X = MaterialEntityTable?.GetDataColumnValues("double:NormalUvOffset.X") ?? Array.Empty(); + MaterialNormalUvOffset_Y = MaterialEntityTable?.GetDataColumnValues("double:NormalUvOffset.Y") ?? Array.Empty(); + MaterialNormalAmount = MaterialEntityTable?.GetDataColumnValues("double:NormalAmount") ?? Array.Empty(); + MaterialGlossiness = MaterialEntityTable?.GetDataColumnValues("double:Glossiness") ?? Array.Empty(); + MaterialSmoothness = MaterialEntityTable?.GetDataColumnValues("double:Smoothness") ?? Array.Empty(); + MaterialTransparency = MaterialEntityTable?.GetDataColumnValues("double:Transparency") ?? Array.Empty(); + MaterialInElementArea = MaterialInElementEntityTable?.GetDataColumnValues("double:Area") ?? Array.Empty(); + MaterialInElementVolume = MaterialInElementEntityTable?.GetDataColumnValues("double:Volume") ?? Array.Empty(); + MaterialInElementIsPaint = MaterialInElementEntityTable?.GetDataColumnValues("byte:IsPaint") ?? Array.Empty(); + CompoundStructureLayerOrderIndex = CompoundStructureLayerEntityTable?.GetDataColumnValues("int:OrderIndex") ?? Array.Empty(); + CompoundStructureLayerWidth = CompoundStructureLayerEntityTable?.GetDataColumnValues("double:Width") ?? Array.Empty(); + CompoundStructureLayerMaterialFunctionAssignment = CompoundStructureLayerEntityTable?.GetStringColumnValues("string:MaterialFunctionAssignment") ?? Array.Empty(); + CompoundStructureWidth = CompoundStructureEntityTable?.GetDataColumnValues("double:Width") ?? Array.Empty(); + GeometryBox_Min_X = GeometryEntityTable?.GetDataColumnValues("float:Box.Min.X") ?? Array.Empty(); + GeometryBox_Min_Y = GeometryEntityTable?.GetDataColumnValues("float:Box.Min.Y") ?? Array.Empty(); + GeometryBox_Min_Z = GeometryEntityTable?.GetDataColumnValues("float:Box.Min.Z") ?? Array.Empty(); + GeometryBox_Max_X = GeometryEntityTable?.GetDataColumnValues("float:Box.Max.X") ?? Array.Empty(); + GeometryBox_Max_Y = GeometryEntityTable?.GetDataColumnValues("float:Box.Max.Y") ?? Array.Empty(); + GeometryBox_Max_Z = GeometryEntityTable?.GetDataColumnValues("float:Box.Max.Z") ?? Array.Empty(); + GeometryVertexCount = GeometryEntityTable?.GetDataColumnValues("int:VertexCount") ?? Array.Empty(); + GeometryFaceCount = GeometryEntityTable?.GetDataColumnValues("int:FaceCount") ?? Array.Empty(); + SystemSystemType = SystemEntityTable?.GetDataColumnValues("int:SystemType") ?? Array.Empty(); + ElementInSystemRoles = ElementInSystemEntityTable?.GetDataColumnValues("int:Roles") ?? Array.Empty(); + WarningGuid = WarningEntityTable?.GetStringColumnValues("string:Guid") ?? Array.Empty(); + WarningSeverity = WarningEntityTable?.GetStringColumnValues("string:Severity") ?? Array.Empty(); + WarningDescription = WarningEntityTable?.GetStringColumnValues("string:Description") ?? Array.Empty(); + BasePointIsSurveyPoint = BasePointEntityTable?.GetDataColumnValues("byte:IsSurveyPoint") ?? Array.Empty(); + BasePointPosition_X = BasePointEntityTable?.GetDataColumnValues("double:Position.X") ?? Array.Empty(); + BasePointPosition_Y = BasePointEntityTable?.GetDataColumnValues("double:Position.Y") ?? Array.Empty(); + BasePointPosition_Z = BasePointEntityTable?.GetDataColumnValues("double:Position.Z") ?? Array.Empty(); + BasePointSharedPosition_X = BasePointEntityTable?.GetDataColumnValues("double:SharedPosition.X") ?? Array.Empty(); + BasePointSharedPosition_Y = BasePointEntityTable?.GetDataColumnValues("double:SharedPosition.Y") ?? Array.Empty(); + BasePointSharedPosition_Z = BasePointEntityTable?.GetDataColumnValues("double:SharedPosition.Z") ?? Array.Empty(); + PhaseFilterNew = PhaseFilterEntityTable?.GetDataColumnValues("int:New") ?? Array.Empty(); + PhaseFilterExisting = PhaseFilterEntityTable?.GetDataColumnValues("int:Existing") ?? Array.Empty(); + PhaseFilterDemolished = PhaseFilterEntityTable?.GetDataColumnValues("int:Demolished") ?? Array.Empty(); + PhaseFilterTemporary = PhaseFilterEntityTable?.GetDataColumnValues("int:Temporary") ?? Array.Empty(); + GridStartPoint_X = GridEntityTable?.GetDataColumnValues("double:StartPoint.X") ?? Array.Empty(); + GridStartPoint_Y = GridEntityTable?.GetDataColumnValues("double:StartPoint.Y") ?? Array.Empty(); + GridStartPoint_Z = GridEntityTable?.GetDataColumnValues("double:StartPoint.Z") ?? Array.Empty(); + GridEndPoint_X = GridEntityTable?.GetDataColumnValues("double:EndPoint.X") ?? Array.Empty(); + GridEndPoint_Y = GridEntityTable?.GetDataColumnValues("double:EndPoint.Y") ?? Array.Empty(); + GridEndPoint_Z = GridEntityTable?.GetDataColumnValues("double:EndPoint.Z") ?? Array.Empty(); + GridIsCurved = GridEntityTable?.GetDataColumnValues("byte:IsCurved") ?? Array.Empty(); + GridExtents_Min_X = GridEntityTable?.GetDataColumnValues("double:Extents.Min.X") ?? Array.Empty(); + GridExtents_Min_Y = GridEntityTable?.GetDataColumnValues("double:Extents.Min.Y") ?? Array.Empty(); + GridExtents_Min_Z = GridEntityTable?.GetDataColumnValues("double:Extents.Min.Z") ?? Array.Empty(); + GridExtents_Max_X = GridEntityTable?.GetDataColumnValues("double:Extents.Max.X") ?? Array.Empty(); + GridExtents_Max_Y = GridEntityTable?.GetDataColumnValues("double:Extents.Max.Y") ?? Array.Empty(); + GridExtents_Max_Z = GridEntityTable?.GetDataColumnValues("double:Extents.Max.Z") ?? Array.Empty(); + AreaValue = AreaEntityTable?.GetDataColumnValues("double:Value") ?? Array.Empty(); + AreaPerimeter = AreaEntityTable?.GetDataColumnValues("double:Perimeter") ?? Array.Empty(); + AreaNumber = AreaEntityTable?.GetStringColumnValues("string:Number") ?? Array.Empty(); + AreaIsGrossInterior = AreaEntityTable?.GetDataColumnValues("byte:IsGrossInterior") ?? Array.Empty(); + AreaSchemeIsGrossBuildingArea = AreaSchemeEntityTable?.GetDataColumnValues("byte:IsGrossBuildingArea") ?? Array.Empty(); + ScheduleColumnName = ScheduleColumnEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty(); + ScheduleColumnColumnIndex = ScheduleColumnEntityTable?.GetDataColumnValues("int:ColumnIndex") ?? Array.Empty(); + ScheduleCellValue = ScheduleCellEntityTable?.GetStringColumnValues("string:Value") ?? Array.Empty(); + ScheduleCellRowIndex = ScheduleCellEntityTable?.GetDataColumnValues("int:RowIndex") ?? Array.Empty(); + SiteLatitude = SiteEntityTable?.GetDataColumnValues("double:Latitude") ?? Array.Empty(); + SiteLongitude = SiteEntityTable?.GetDataColumnValues("double:Longitude") ?? Array.Empty(); + SiteAddress = SiteEntityTable?.GetStringColumnValues("string:Address") ?? Array.Empty(); + SiteElevation = SiteEntityTable?.GetDataColumnValues("double:Elevation") ?? Array.Empty(); + SiteNumber = SiteEntityTable?.GetStringColumnValues("string:Number") ?? Array.Empty(); + BuildingElevation = BuildingEntityTable?.GetDataColumnValues("double:Elevation") ?? Array.Empty(); + BuildingTerrainElevation = BuildingEntityTable?.GetDataColumnValues("double:TerrainElevation") ?? Array.Empty(); + BuildingAddress = BuildingEntityTable?.GetStringColumnValues("string:Address") ?? Array.Empty(); // Initialize entity relational columns - ParameterDescriptorDisplayUnitIndex = ParameterDescriptorEntityTable?.GetIndexColumnValues("index:Vim.DisplayUnit:DisplayUnit") ?? Array.Empty().ToIArray(); - ParameterParameterDescriptorIndex = ParameterEntityTable?.GetIndexColumnValues("index:Vim.ParameterDescriptor:ParameterDescriptor") ?? Array.Empty().ToIArray(); - ParameterElementIndex = ParameterEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ElementLevelIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Level:Level") ?? Array.Empty().ToIArray(); - ElementPhaseCreatedIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Phase:PhaseCreated") ?? Array.Empty().ToIArray(); - ElementPhaseDemolishedIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Phase:PhaseDemolished") ?? Array.Empty().ToIArray(); - ElementCategoryIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Category:Category") ?? Array.Empty().ToIArray(); - ElementWorksetIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Workset:Workset") ?? Array.Empty().ToIArray(); - ElementDesignOptionIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.DesignOption:DesignOption") ?? Array.Empty().ToIArray(); - ElementOwnerViewIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.View:OwnerView") ?? Array.Empty().ToIArray(); - ElementGroupIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Group:Group") ?? Array.Empty().ToIArray(); - ElementAssemblyInstanceIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.AssemblyInstance:AssemblyInstance") ?? Array.Empty().ToIArray(); - ElementBimDocumentIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty().ToIArray(); - ElementRoomIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Room:Room") ?? Array.Empty().ToIArray(); - WorksetBimDocumentIndex = WorksetEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty().ToIArray(); - AssemblyInstanceElementIndex = AssemblyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - GroupElementIndex = GroupEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - DesignOptionElementIndex = DesignOptionEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - LevelFamilyTypeIndex = LevelEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty().ToIArray(); - LevelBuildingIndex = LevelEntityTable?.GetIndexColumnValues("index:Vim.Building:Building") ?? Array.Empty().ToIArray(); - LevelElementIndex = LevelEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - PhaseElementIndex = PhaseEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - RoomUpperLimitIndex = RoomEntityTable?.GetIndexColumnValues("index:Vim.Level:UpperLimit") ?? Array.Empty().ToIArray(); - RoomElementIndex = RoomEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - BimDocumentActiveViewIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.View:ActiveView") ?? Array.Empty().ToIArray(); - BimDocumentOwnerFamilyIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.Family:OwnerFamily") ?? Array.Empty().ToIArray(); - BimDocumentParentIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:Parent") ?? Array.Empty().ToIArray(); - BimDocumentElementIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - DisplayUnitInBimDocumentDisplayUnitIndex = DisplayUnitInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.DisplayUnit:DisplayUnit") ?? Array.Empty().ToIArray(); - DisplayUnitInBimDocumentBimDocumentIndex = DisplayUnitInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty().ToIArray(); - PhaseOrderInBimDocumentPhaseIndex = PhaseOrderInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.Phase:Phase") ?? Array.Empty().ToIArray(); - PhaseOrderInBimDocumentBimDocumentIndex = PhaseOrderInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty().ToIArray(); - CategoryParentIndex = CategoryEntityTable?.GetIndexColumnValues("index:Vim.Category:Parent") ?? Array.Empty().ToIArray(); - CategoryMaterialIndex = CategoryEntityTable?.GetIndexColumnValues("index:Vim.Material:Material") ?? Array.Empty().ToIArray(); - FamilyFamilyCategoryIndex = FamilyEntityTable?.GetIndexColumnValues("index:Vim.Category:FamilyCategory") ?? Array.Empty().ToIArray(); - FamilyElementIndex = FamilyEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - FamilyTypeFamilyIndex = FamilyTypeEntityTable?.GetIndexColumnValues("index:Vim.Family:Family") ?? Array.Empty().ToIArray(); - FamilyTypeCompoundStructureIndex = FamilyTypeEntityTable?.GetIndexColumnValues("index:Vim.CompoundStructure:CompoundStructure") ?? Array.Empty().ToIArray(); - FamilyTypeElementIndex = FamilyTypeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - FamilyInstanceFamilyTypeIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty().ToIArray(); - FamilyInstanceHostIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:Host") ?? Array.Empty().ToIArray(); - FamilyInstanceFromRoomIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Room:FromRoom") ?? Array.Empty().ToIArray(); - FamilyInstanceToRoomIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Room:ToRoom") ?? Array.Empty().ToIArray(); - FamilyInstanceSuperComponentIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:SuperComponent") ?? Array.Empty().ToIArray(); - FamilyInstanceElementIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ViewCameraIndex = ViewEntityTable?.GetIndexColumnValues("index:Vim.Camera:Camera") ?? Array.Empty().ToIArray(); - ViewFamilyTypeIndex = ViewEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty().ToIArray(); - ViewElementIndex = ViewEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ElementInViewViewIndex = ElementInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty().ToIArray(); - ElementInViewElementIndex = ElementInViewEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ShapeInViewShapeIndex = ShapeInViewEntityTable?.GetIndexColumnValues("index:Vim.Shape:Shape") ?? Array.Empty().ToIArray(); - ShapeInViewViewIndex = ShapeInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty().ToIArray(); - AssetInViewAssetIndex = AssetInViewEntityTable?.GetIndexColumnValues("index:Vim.Asset:Asset") ?? Array.Empty().ToIArray(); - AssetInViewViewIndex = AssetInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty().ToIArray(); - AssetInViewSheetAssetIndex = AssetInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.Asset:Asset") ?? Array.Empty().ToIArray(); - AssetInViewSheetViewSheetIndex = AssetInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheet:ViewSheet") ?? Array.Empty().ToIArray(); - LevelInViewLevelIndex = LevelInViewEntityTable?.GetIndexColumnValues("index:Vim.Level:Level") ?? Array.Empty().ToIArray(); - LevelInViewViewIndex = LevelInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty().ToIArray(); - MaterialColorTextureFileIndex = MaterialEntityTable?.GetIndexColumnValues("index:Vim.Asset:ColorTextureFile") ?? Array.Empty().ToIArray(); - MaterialNormalTextureFileIndex = MaterialEntityTable?.GetIndexColumnValues("index:Vim.Asset:NormalTextureFile") ?? Array.Empty().ToIArray(); - MaterialElementIndex = MaterialEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - MaterialInElementMaterialIndex = MaterialInElementEntityTable?.GetIndexColumnValues("index:Vim.Material:Material") ?? Array.Empty().ToIArray(); - MaterialInElementElementIndex = MaterialInElementEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - CompoundStructureLayerMaterialIndex = CompoundStructureLayerEntityTable?.GetIndexColumnValues("index:Vim.Material:Material") ?? Array.Empty().ToIArray(); - CompoundStructureLayerCompoundStructureIndex = CompoundStructureLayerEntityTable?.GetIndexColumnValues("index:Vim.CompoundStructure:CompoundStructure") ?? Array.Empty().ToIArray(); - CompoundStructureStructuralLayerIndex = CompoundStructureEntityTable?.GetIndexColumnValues("index:Vim.CompoundStructureLayer:StructuralLayer") ?? Array.Empty().ToIArray(); - NodeElementIndex = NodeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ShapeElementIndex = ShapeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ShapeCollectionElementIndex = ShapeCollectionEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ShapeInShapeCollectionShapeIndex = ShapeInShapeCollectionEntityTable?.GetIndexColumnValues("index:Vim.Shape:Shape") ?? Array.Empty().ToIArray(); - ShapeInShapeCollectionShapeCollectionIndex = ShapeInShapeCollectionEntityTable?.GetIndexColumnValues("index:Vim.ShapeCollection:ShapeCollection") ?? Array.Empty().ToIArray(); - SystemFamilyTypeIndex = SystemEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty().ToIArray(); - SystemElementIndex = SystemEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ElementInSystemSystemIndex = ElementInSystemEntityTable?.GetIndexColumnValues("index:Vim.System:System") ?? Array.Empty().ToIArray(); - ElementInSystemElementIndex = ElementInSystemEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - WarningBimDocumentIndex = WarningEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty().ToIArray(); - ElementInWarningWarningIndex = ElementInWarningEntityTable?.GetIndexColumnValues("index:Vim.Warning:Warning") ?? Array.Empty().ToIArray(); - ElementInWarningElementIndex = ElementInWarningEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - BasePointElementIndex = BasePointEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - PhaseFilterElementIndex = PhaseFilterEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - GridFamilyTypeIndex = GridEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty().ToIArray(); - GridElementIndex = GridEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - AreaAreaSchemeIndex = AreaEntityTable?.GetIndexColumnValues("index:Vim.AreaScheme:AreaScheme") ?? Array.Empty().ToIArray(); - AreaElementIndex = AreaEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - AreaSchemeElementIndex = AreaSchemeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ScheduleElementIndex = ScheduleEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ScheduleColumnScheduleIndex = ScheduleColumnEntityTable?.GetIndexColumnValues("index:Vim.Schedule:Schedule") ?? Array.Empty().ToIArray(); - ScheduleCellScheduleColumnIndex = ScheduleCellEntityTable?.GetIndexColumnValues("index:Vim.ScheduleColumn:ScheduleColumn") ?? Array.Empty().ToIArray(); - ViewSheetSetElementIndex = ViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ViewSheetFamilyTypeIndex = ViewSheetEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty().ToIArray(); - ViewSheetElementIndex = ViewSheetEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ViewSheetInViewSheetSetViewSheetIndex = ViewSheetInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheet:ViewSheet") ?? Array.Empty().ToIArray(); - ViewSheetInViewSheetSetViewSheetSetIndex = ViewSheetInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheetSet:ViewSheetSet") ?? Array.Empty().ToIArray(); - ViewInViewSheetSetViewIndex = ViewInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty().ToIArray(); - ViewInViewSheetSetViewSheetSetIndex = ViewInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheetSet:ViewSheetSet") ?? Array.Empty().ToIArray(); - ViewInViewSheetViewIndex = ViewInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty().ToIArray(); - ViewInViewSheetViewSheetIndex = ViewInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheet:ViewSheet") ?? Array.Empty().ToIArray(); - SiteElementIndex = SiteEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - BuildingSiteIndex = BuildingEntityTable?.GetIndexColumnValues("index:Vim.Site:Site") ?? Array.Empty().ToIArray(); - BuildingElementIndex = BuildingEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); + ParameterDescriptorDisplayUnitIndex = ParameterDescriptorEntityTable?.GetIndexColumnValues("index:Vim.DisplayUnit:DisplayUnit") ?? Array.Empty(); + ParameterParameterDescriptorIndex = ParameterEntityTable?.GetIndexColumnValues("index:Vim.ParameterDescriptor:ParameterDescriptor") ?? Array.Empty(); + ParameterElementIndex = ParameterEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ElementLevelIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Level:Level") ?? Array.Empty(); + ElementPhaseCreatedIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Phase:PhaseCreated") ?? Array.Empty(); + ElementPhaseDemolishedIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Phase:PhaseDemolished") ?? Array.Empty(); + ElementCategoryIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Category:Category") ?? Array.Empty(); + ElementWorksetIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Workset:Workset") ?? Array.Empty(); + ElementDesignOptionIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.DesignOption:DesignOption") ?? Array.Empty(); + ElementOwnerViewIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.View:OwnerView") ?? Array.Empty(); + ElementGroupIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Group:Group") ?? Array.Empty(); + ElementAssemblyInstanceIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.AssemblyInstance:AssemblyInstance") ?? Array.Empty(); + ElementBimDocumentIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty(); + ElementRoomIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Room:Room") ?? Array.Empty(); + WorksetBimDocumentIndex = WorksetEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty(); + AssemblyInstanceElementIndex = AssemblyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + GroupElementIndex = GroupEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + DesignOptionElementIndex = DesignOptionEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + LevelFamilyTypeIndex = LevelEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty(); + LevelBuildingIndex = LevelEntityTable?.GetIndexColumnValues("index:Vim.Building:Building") ?? Array.Empty(); + LevelElementIndex = LevelEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + PhaseElementIndex = PhaseEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + RoomUpperLimitIndex = RoomEntityTable?.GetIndexColumnValues("index:Vim.Level:UpperLimit") ?? Array.Empty(); + RoomElementIndex = RoomEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + BimDocumentActiveViewIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.View:ActiveView") ?? Array.Empty(); + BimDocumentOwnerFamilyIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.Family:OwnerFamily") ?? Array.Empty(); + BimDocumentParentIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:Parent") ?? Array.Empty(); + BimDocumentElementIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + DisplayUnitInBimDocumentDisplayUnitIndex = DisplayUnitInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.DisplayUnit:DisplayUnit") ?? Array.Empty(); + DisplayUnitInBimDocumentBimDocumentIndex = DisplayUnitInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty(); + PhaseOrderInBimDocumentPhaseIndex = PhaseOrderInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.Phase:Phase") ?? Array.Empty(); + PhaseOrderInBimDocumentBimDocumentIndex = PhaseOrderInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty(); + CategoryParentIndex = CategoryEntityTable?.GetIndexColumnValues("index:Vim.Category:Parent") ?? Array.Empty(); + CategoryMaterialIndex = CategoryEntityTable?.GetIndexColumnValues("index:Vim.Material:Material") ?? Array.Empty(); + FamilyFamilyCategoryIndex = FamilyEntityTable?.GetIndexColumnValues("index:Vim.Category:FamilyCategory") ?? Array.Empty(); + FamilyElementIndex = FamilyEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + FamilyTypeFamilyIndex = FamilyTypeEntityTable?.GetIndexColumnValues("index:Vim.Family:Family") ?? Array.Empty(); + FamilyTypeCompoundStructureIndex = FamilyTypeEntityTable?.GetIndexColumnValues("index:Vim.CompoundStructure:CompoundStructure") ?? Array.Empty(); + FamilyTypeElementIndex = FamilyTypeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + FamilyInstanceFamilyTypeIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty(); + FamilyInstanceHostIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:Host") ?? Array.Empty(); + FamilyInstanceFromRoomIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Room:FromRoom") ?? Array.Empty(); + FamilyInstanceToRoomIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Room:ToRoom") ?? Array.Empty(); + FamilyInstanceSuperComponentIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:SuperComponent") ?? Array.Empty(); + FamilyInstanceElementIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ViewCameraIndex = ViewEntityTable?.GetIndexColumnValues("index:Vim.Camera:Camera") ?? Array.Empty(); + ViewFamilyTypeIndex = ViewEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty(); + ViewElementIndex = ViewEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ElementInViewViewIndex = ElementInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty(); + ElementInViewElementIndex = ElementInViewEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ShapeInViewShapeIndex = ShapeInViewEntityTable?.GetIndexColumnValues("index:Vim.Shape:Shape") ?? Array.Empty(); + ShapeInViewViewIndex = ShapeInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty(); + AssetInViewAssetIndex = AssetInViewEntityTable?.GetIndexColumnValues("index:Vim.Asset:Asset") ?? Array.Empty(); + AssetInViewViewIndex = AssetInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty(); + AssetInViewSheetAssetIndex = AssetInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.Asset:Asset") ?? Array.Empty(); + AssetInViewSheetViewSheetIndex = AssetInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheet:ViewSheet") ?? Array.Empty(); + LevelInViewLevelIndex = LevelInViewEntityTable?.GetIndexColumnValues("index:Vim.Level:Level") ?? Array.Empty(); + LevelInViewViewIndex = LevelInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty(); + MaterialColorTextureFileIndex = MaterialEntityTable?.GetIndexColumnValues("index:Vim.Asset:ColorTextureFile") ?? Array.Empty(); + MaterialNormalTextureFileIndex = MaterialEntityTable?.GetIndexColumnValues("index:Vim.Asset:NormalTextureFile") ?? Array.Empty(); + MaterialElementIndex = MaterialEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + MaterialInElementMaterialIndex = MaterialInElementEntityTable?.GetIndexColumnValues("index:Vim.Material:Material") ?? Array.Empty(); + MaterialInElementElementIndex = MaterialInElementEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + CompoundStructureLayerMaterialIndex = CompoundStructureLayerEntityTable?.GetIndexColumnValues("index:Vim.Material:Material") ?? Array.Empty(); + CompoundStructureLayerCompoundStructureIndex = CompoundStructureLayerEntityTable?.GetIndexColumnValues("index:Vim.CompoundStructure:CompoundStructure") ?? Array.Empty(); + CompoundStructureStructuralLayerIndex = CompoundStructureEntityTable?.GetIndexColumnValues("index:Vim.CompoundStructureLayer:StructuralLayer") ?? Array.Empty(); + NodeElementIndex = NodeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ShapeElementIndex = ShapeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ShapeCollectionElementIndex = ShapeCollectionEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ShapeInShapeCollectionShapeIndex = ShapeInShapeCollectionEntityTable?.GetIndexColumnValues("index:Vim.Shape:Shape") ?? Array.Empty(); + ShapeInShapeCollectionShapeCollectionIndex = ShapeInShapeCollectionEntityTable?.GetIndexColumnValues("index:Vim.ShapeCollection:ShapeCollection") ?? Array.Empty(); + SystemFamilyTypeIndex = SystemEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty(); + SystemElementIndex = SystemEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ElementInSystemSystemIndex = ElementInSystemEntityTable?.GetIndexColumnValues("index:Vim.System:System") ?? Array.Empty(); + ElementInSystemElementIndex = ElementInSystemEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + WarningBimDocumentIndex = WarningEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty(); + ElementInWarningWarningIndex = ElementInWarningEntityTable?.GetIndexColumnValues("index:Vim.Warning:Warning") ?? Array.Empty(); + ElementInWarningElementIndex = ElementInWarningEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + BasePointElementIndex = BasePointEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + PhaseFilterElementIndex = PhaseFilterEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + GridFamilyTypeIndex = GridEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty(); + GridElementIndex = GridEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + AreaAreaSchemeIndex = AreaEntityTable?.GetIndexColumnValues("index:Vim.AreaScheme:AreaScheme") ?? Array.Empty(); + AreaElementIndex = AreaEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + AreaSchemeElementIndex = AreaSchemeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ScheduleElementIndex = ScheduleEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ScheduleColumnScheduleIndex = ScheduleColumnEntityTable?.GetIndexColumnValues("index:Vim.Schedule:Schedule") ?? Array.Empty(); + ScheduleCellScheduleColumnIndex = ScheduleCellEntityTable?.GetIndexColumnValues("index:Vim.ScheduleColumn:ScheduleColumn") ?? Array.Empty(); + ViewSheetSetElementIndex = ViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ViewSheetFamilyTypeIndex = ViewSheetEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty(); + ViewSheetElementIndex = ViewSheetEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ViewSheetInViewSheetSetViewSheetIndex = ViewSheetInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheet:ViewSheet") ?? Array.Empty(); + ViewSheetInViewSheetSetViewSheetSetIndex = ViewSheetInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheetSet:ViewSheetSet") ?? Array.Empty(); + ViewInViewSheetSetViewIndex = ViewInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty(); + ViewInViewSheetSetViewSheetSetIndex = ViewInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheetSet:ViewSheetSet") ?? Array.Empty(); + ViewInViewSheetViewIndex = ViewInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty(); + ViewInViewSheetViewSheetIndex = ViewInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheet:ViewSheet") ?? Array.Empty(); + SiteElementIndex = SiteEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + BuildingSiteIndex = BuildingEntityTable?.GetIndexColumnValues("index:Vim.Site:Site") ?? Array.Empty(); + BuildingElementIndex = BuildingEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); // Initialize entity collections - AssetList = NumAsset.Select(i => GetAsset(i)); - DisplayUnitList = NumDisplayUnit.Select(i => GetDisplayUnit(i)); - ParameterDescriptorList = NumParameterDescriptor.Select(i => GetParameterDescriptor(i)); - ParameterList = NumParameter.Select(i => GetParameter(i)); - ElementList = NumElement.Select(i => GetElement(i)); - WorksetList = NumWorkset.Select(i => GetWorkset(i)); - AssemblyInstanceList = NumAssemblyInstance.Select(i => GetAssemblyInstance(i)); - GroupList = NumGroup.Select(i => GetGroup(i)); - DesignOptionList = NumDesignOption.Select(i => GetDesignOption(i)); - LevelList = NumLevel.Select(i => GetLevel(i)); - PhaseList = NumPhase.Select(i => GetPhase(i)); - RoomList = NumRoom.Select(i => GetRoom(i)); - BimDocumentList = NumBimDocument.Select(i => GetBimDocument(i)); - DisplayUnitInBimDocumentList = NumDisplayUnitInBimDocument.Select(i => GetDisplayUnitInBimDocument(i)); - PhaseOrderInBimDocumentList = NumPhaseOrderInBimDocument.Select(i => GetPhaseOrderInBimDocument(i)); - CategoryList = NumCategory.Select(i => GetCategory(i)); - FamilyList = NumFamily.Select(i => GetFamily(i)); - FamilyTypeList = NumFamilyType.Select(i => GetFamilyType(i)); - FamilyInstanceList = NumFamilyInstance.Select(i => GetFamilyInstance(i)); - ViewList = NumView.Select(i => GetView(i)); - ElementInViewList = NumElementInView.Select(i => GetElementInView(i)); - ShapeInViewList = NumShapeInView.Select(i => GetShapeInView(i)); - AssetInViewList = NumAssetInView.Select(i => GetAssetInView(i)); - AssetInViewSheetList = NumAssetInViewSheet.Select(i => GetAssetInViewSheet(i)); - LevelInViewList = NumLevelInView.Select(i => GetLevelInView(i)); - CameraList = NumCamera.Select(i => GetCamera(i)); - MaterialList = NumMaterial.Select(i => GetMaterial(i)); - MaterialInElementList = NumMaterialInElement.Select(i => GetMaterialInElement(i)); - CompoundStructureLayerList = NumCompoundStructureLayer.Select(i => GetCompoundStructureLayer(i)); - CompoundStructureList = NumCompoundStructure.Select(i => GetCompoundStructure(i)); - NodeList = NumNode.Select(i => GetNode(i)); - GeometryList = NumGeometry.Select(i => GetGeometry(i)); - ShapeList = NumShape.Select(i => GetShape(i)); - ShapeCollectionList = NumShapeCollection.Select(i => GetShapeCollection(i)); - ShapeInShapeCollectionList = NumShapeInShapeCollection.Select(i => GetShapeInShapeCollection(i)); - SystemList = NumSystem.Select(i => GetSystem(i)); - ElementInSystemList = NumElementInSystem.Select(i => GetElementInSystem(i)); - WarningList = NumWarning.Select(i => GetWarning(i)); - ElementInWarningList = NumElementInWarning.Select(i => GetElementInWarning(i)); - BasePointList = NumBasePoint.Select(i => GetBasePoint(i)); - PhaseFilterList = NumPhaseFilter.Select(i => GetPhaseFilter(i)); - GridList = NumGrid.Select(i => GetGrid(i)); - AreaList = NumArea.Select(i => GetArea(i)); - AreaSchemeList = NumAreaScheme.Select(i => GetAreaScheme(i)); - ScheduleList = NumSchedule.Select(i => GetSchedule(i)); - ScheduleColumnList = NumScheduleColumn.Select(i => GetScheduleColumn(i)); - ScheduleCellList = NumScheduleCell.Select(i => GetScheduleCell(i)); - ViewSheetSetList = NumViewSheetSet.Select(i => GetViewSheetSet(i)); - ViewSheetList = NumViewSheet.Select(i => GetViewSheet(i)); - ViewSheetInViewSheetSetList = NumViewSheetInViewSheetSet.Select(i => GetViewSheetInViewSheetSet(i)); - ViewInViewSheetSetList = NumViewInViewSheetSet.Select(i => GetViewInViewSheetSet(i)); - ViewInViewSheetList = NumViewInViewSheet.Select(i => GetViewInViewSheet(i)); - SiteList = NumSite.Select(i => GetSite(i)); - BuildingList = NumBuilding.Select(i => GetBuilding(i)); + AssetList = Enumerable.Range(0, NumAsset).Select(i => GetAsset(i)).ToArray(); + DisplayUnitList = Enumerable.Range(0, NumDisplayUnit).Select(i => GetDisplayUnit(i)).ToArray(); + ParameterDescriptorList = Enumerable.Range(0, NumParameterDescriptor).Select(i => GetParameterDescriptor(i)).ToArray(); + ParameterList = Enumerable.Range(0, NumParameter).Select(i => GetParameter(i)).ToArray(); + ElementList = Enumerable.Range(0, NumElement).Select(i => GetElement(i)).ToArray(); + WorksetList = Enumerable.Range(0, NumWorkset).Select(i => GetWorkset(i)).ToArray(); + AssemblyInstanceList = Enumerable.Range(0, NumAssemblyInstance).Select(i => GetAssemblyInstance(i)).ToArray(); + GroupList = Enumerable.Range(0, NumGroup).Select(i => GetGroup(i)).ToArray(); + DesignOptionList = Enumerable.Range(0, NumDesignOption).Select(i => GetDesignOption(i)).ToArray(); + LevelList = Enumerable.Range(0, NumLevel).Select(i => GetLevel(i)).ToArray(); + PhaseList = Enumerable.Range(0, NumPhase).Select(i => GetPhase(i)).ToArray(); + RoomList = Enumerable.Range(0, NumRoom).Select(i => GetRoom(i)).ToArray(); + BimDocumentList = Enumerable.Range(0, NumBimDocument).Select(i => GetBimDocument(i)).ToArray(); + DisplayUnitInBimDocumentList = Enumerable.Range(0, NumDisplayUnitInBimDocument).Select(i => GetDisplayUnitInBimDocument(i)).ToArray(); + PhaseOrderInBimDocumentList = Enumerable.Range(0, NumPhaseOrderInBimDocument).Select(i => GetPhaseOrderInBimDocument(i)).ToArray(); + CategoryList = Enumerable.Range(0, NumCategory).Select(i => GetCategory(i)).ToArray(); + FamilyList = Enumerable.Range(0, NumFamily).Select(i => GetFamily(i)).ToArray(); + FamilyTypeList = Enumerable.Range(0, NumFamilyType).Select(i => GetFamilyType(i)).ToArray(); + FamilyInstanceList = Enumerable.Range(0, NumFamilyInstance).Select(i => GetFamilyInstance(i)).ToArray(); + ViewList = Enumerable.Range(0, NumView).Select(i => GetView(i)).ToArray(); + ElementInViewList = Enumerable.Range(0, NumElementInView).Select(i => GetElementInView(i)).ToArray(); + ShapeInViewList = Enumerable.Range(0, NumShapeInView).Select(i => GetShapeInView(i)).ToArray(); + AssetInViewList = Enumerable.Range(0, NumAssetInView).Select(i => GetAssetInView(i)).ToArray(); + AssetInViewSheetList = Enumerable.Range(0, NumAssetInViewSheet).Select(i => GetAssetInViewSheet(i)).ToArray(); + LevelInViewList = Enumerable.Range(0, NumLevelInView).Select(i => GetLevelInView(i)).ToArray(); + CameraList = Enumerable.Range(0, NumCamera).Select(i => GetCamera(i)).ToArray(); + MaterialList = Enumerable.Range(0, NumMaterial).Select(i => GetMaterial(i)).ToArray(); + MaterialInElementList = Enumerable.Range(0, NumMaterialInElement).Select(i => GetMaterialInElement(i)).ToArray(); + CompoundStructureLayerList = Enumerable.Range(0, NumCompoundStructureLayer).Select(i => GetCompoundStructureLayer(i)).ToArray(); + CompoundStructureList = Enumerable.Range(0, NumCompoundStructure).Select(i => GetCompoundStructure(i)).ToArray(); + NodeList = Enumerable.Range(0, NumNode).Select(i => GetNode(i)).ToArray(); + GeometryList = Enumerable.Range(0, NumGeometry).Select(i => GetGeometry(i)).ToArray(); + ShapeList = Enumerable.Range(0, NumShape).Select(i => GetShape(i)).ToArray(); + ShapeCollectionList = Enumerable.Range(0, NumShapeCollection).Select(i => GetShapeCollection(i)).ToArray(); + ShapeInShapeCollectionList = Enumerable.Range(0, NumShapeInShapeCollection).Select(i => GetShapeInShapeCollection(i)).ToArray(); + SystemList = Enumerable.Range(0, NumSystem).Select(i => GetSystem(i)).ToArray(); + ElementInSystemList = Enumerable.Range(0, NumElementInSystem).Select(i => GetElementInSystem(i)).ToArray(); + WarningList = Enumerable.Range(0, NumWarning).Select(i => GetWarning(i)).ToArray(); + ElementInWarningList = Enumerable.Range(0, NumElementInWarning).Select(i => GetElementInWarning(i)).ToArray(); + BasePointList = Enumerable.Range(0, NumBasePoint).Select(i => GetBasePoint(i)).ToArray(); + PhaseFilterList = Enumerable.Range(0, NumPhaseFilter).Select(i => GetPhaseFilter(i)).ToArray(); + GridList = Enumerable.Range(0, NumGrid).Select(i => GetGrid(i)).ToArray(); + AreaList = Enumerable.Range(0, NumArea).Select(i => GetArea(i)).ToArray(); + AreaSchemeList = Enumerable.Range(0, NumAreaScheme).Select(i => GetAreaScheme(i)).ToArray(); + ScheduleList = Enumerable.Range(0, NumSchedule).Select(i => GetSchedule(i)).ToArray(); + ScheduleColumnList = Enumerable.Range(0, NumScheduleColumn).Select(i => GetScheduleColumn(i)).ToArray(); + ScheduleCellList = Enumerable.Range(0, NumScheduleCell).Select(i => GetScheduleCell(i)).ToArray(); + ViewSheetSetList = Enumerable.Range(0, NumViewSheetSet).Select(i => GetViewSheetSet(i)).ToArray(); + ViewSheetList = Enumerable.Range(0, NumViewSheet).Select(i => GetViewSheet(i)).ToArray(); + ViewSheetInViewSheetSetList = Enumerable.Range(0, NumViewSheetInViewSheetSet).Select(i => GetViewSheetInViewSheetSet(i)).ToArray(); + ViewInViewSheetSetList = Enumerable.Range(0, NumViewInViewSheetSet).Select(i => GetViewInViewSheetSet(i)).ToArray(); + ViewInViewSheetList = Enumerable.Range(0, NumViewInViewSheet).Select(i => GetViewInViewSheet(i)).ToArray(); + SiteList = Enumerable.Range(0, NumSite).Select(i => GetSite(i)).ToArray(); + BuildingList = Enumerable.Range(0, NumBuilding).Select(i => GetBuilding(i)).ToArray(); // Initialize element index maps ElementIndexMaps = new ElementIndexMaps(this, inParallel); diff --git a/src/cs/vim/Vim.Format/ObjectModel/Urn.cs b/src/cs/vim/Vim.Format/ObjectModel/Urn.cs index 70e96b78..b5039d2d 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/Urn.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/Urn.cs @@ -22,14 +22,14 @@ public static class Urn // urn:epc:id:sscc:0614141.1234567890 Serial Shipping Container Code // - public const string Separator = ":"; + private const string Separator = ":"; public const string VimNID = "vim"; - public const string SystemPrefix = "sys"; - public const string DocumentPrefix = "doc"; - public const string ElementPrefix = "elem"; - public const string Null = "null"; + private const string SystemPrefix = "sys"; + private const string DocumentPrefix = "doc"; + private const string ElementPrefix = "elem"; + private const string Null = "null"; - public static string CreateUrn(string nid, params string[] nss) + private static string CreateUrn(string nid, params string[] nss) => string.Join(Separator, new[] {"urn", nid}.Concat(nss)); // Context-specific helpers @@ -40,7 +40,7 @@ public static string GetSystemUrn(string nid, string value) public static string GetBimDocumentUrn(string nid, string guid, int numSaves) => CreateUrn(nid, DocumentPrefix, guid, numSaves.ToString()); - public static string GetBimDocumentUrn(string nid, BimDocument bimDocument) + private static string GetBimDocumentUrn(string nid, BimDocument bimDocument) => GetBimDocumentUrn(nid, bimDocument?.Guid ?? Null, bimDocument?.NumSaves ?? default); public static string GetElementUrn(string documentUrn, int elementId) @@ -49,9 +49,6 @@ public static string GetElementUrn(string documentUrn, int elementId) public static string GetElementUrn(string documentUrn, long elementId) => documentUrn + Separator + ElementPrefix + Separator + elementId; - public static string GetElementUrn(string nid, string documentGuid, int documentNumSaves, int elementId) - => GetElementUrn(GetBimDocumentUrn(nid, documentGuid, documentNumSaves), elementId); - public static string GetElementUrn(string nid, Element element) => GetElementUrn(GetBimDocumentUrn(nid, element?.BimDocument), element?.Id ?? -1); } diff --git a/src/cs/vim/Vim.Format/ObjectModel/Validation.cs b/src/cs/vim/Vim.Format/ObjectModel/Validation.cs index 24fb6399..84caa729 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/Validation.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/Validation.cs @@ -3,8 +3,8 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; -using Vim.LinqArray; using Vim.Math3d; +using Vim.Util; namespace Vim.Format.ObjectModel { @@ -52,13 +52,13 @@ public ObjectModelValidationException(string message) : base(message) { } public static class Validation { - public static void ValidateBimDocument(this DocumentModel dm, ObjectModelValidationOptions validationOptions) + private static void ValidateBimDocument(this DocumentModel dm, ObjectModelValidationOptions validationOptions) { // There is at least one BimDocument in the document model. if (dm.NumBimDocument == 0 && validationOptions.BimDocumentMustExist) throw new ObjectModelValidationException($"No {nameof(BimDocument)} found."); - foreach (var bd in dm.BimDocumentList.ToEnumerable()) + foreach (var bd in dm.BimDocumentList) { var bdElement = bd.Element; if (bdElement == null) @@ -78,14 +78,14 @@ public static void ValidateBimDocument(this DocumentModel dm, ObjectModelValidat } } - public static void ValidateCompoundStructureLayer(this CompoundStructureLayer layer) + private static void ValidateCompoundStructureLayer(this CompoundStructureLayer layer) { // All CompoundLayers have a CompoundStructure if (layer.CompoundStructure == null) throw new ObjectModelValidationException($"{nameof(CompoundStructureLayer)} {layer.Index} has null {nameof(CompoundStructure)}"); } - public static void ValidateCompoundStructures(this DocumentModel dm) + private static void ValidateCompoundStructures(this DocumentModel dm) { var cslArray = dm.CompoundStructureLayerList.ToArray(); @@ -136,21 +136,21 @@ public static void ValidateCompoundStructures(this DocumentModel dm) throw new ObjectModelValidationException($"A {nameof(CompoundStructure)} must be referenced by exactly one {nameof(FamilyType)}."); } - public static void ValidateAssets(this DocumentModel dm) + private static void ValidateAssets(this DocumentModel dm) { // Validate that the assets contained in the buffers matches the assets entity table. var assetBuffers = dm.Document.Assets; var assetEntities = dm.AssetList.ToArray(); foreach (var asset in assetEntities) { - if (!assetBuffers.Contains(asset.BufferName)) + if (!assetBuffers.ContainsKey(asset.BufferName)) throw new ObjectModelValidationException($"No matching asset buffer found for asset entity {asset.Index} with {nameof(asset.BufferName)} '{asset.BufferName}'"); } } - public static void ValidateParameters(this DocumentModel dm) + private static void ValidateParameters(this DocumentModel dm) { - Parallel.ForEach(dm.ParameterList.ToEnumerable(), p => + Parallel.ForEach(dm.ParameterList, p => { // Each parameter must be associated to an element. if (p._Element.Index == EntityRelation.None) @@ -162,14 +162,14 @@ public static void ValidateParameters(this DocumentModel dm) }); // Validate the parameter descriptors. - foreach (var pd in dm.ParameterDescriptorList.ToEnumerable()) + foreach (var pd in dm.ParameterDescriptorList) { if (pd.DisplayUnit == null) throw new ObjectModelValidationException($"{nameof(DisplayUnit)} is null for {nameof(ParameterDescriptor)} {pd.Index}"); } } - public static void ValidatePhases(this DocumentModel dm) + private static void ValidatePhases(this DocumentModel dm) { // Validate the phase order information. var poArray = dm.PhaseOrderInBimDocumentList.ToArray(); @@ -198,7 +198,7 @@ public static void ValidatePhases(this DocumentModel dm) } // Validate that the phase order information covers the set of phases. - var phaseIndexSet = new HashSet(dm.PhaseList.Select(p => p.Index).ToEnumerable()); + var phaseIndexSet = new HashSet(dm.PhaseList.Select(p => p.Index)); phaseIndexSet.ExceptWith(poArray.Select(po => po.Index)); if (phaseIndexSet.Count != 0) throw new ObjectModelValidationException($"{nameof(Phase)} index coverage is incomplete among {nameof(PhaseOrderInBimDocument)}"); @@ -207,7 +207,7 @@ public static void ValidatePhases(this DocumentModel dm) /// /// Generic storage key check; ensures that the keys only appear once in the keySet. /// - public static void ValidateStorageKeys(IEnumerable storageKeyEntities) + private static void ValidateStorageKeys(IEnumerable storageKeyEntities) { var keySet = new HashSet(); foreach (var entity in storageKeyEntities) @@ -221,7 +221,7 @@ public static void ValidateStorageKeys(IEnumerable storageKeyEntiti /// /// Validate that the entities which inherit from IStorageKey all have unique storage key values. /// - public static void ValidateStorageKeys(this DocumentModel dm) + private static void ValidateStorageKeys(this DocumentModel dm) { // ReSharper disable once SuspiciousTypeConversion.Global var storageKeyEntityLists = dm.AllEntities @@ -232,7 +232,7 @@ public static void ValidateStorageKeys(this DocumentModel dm) } } - public static void ValidateMaterials(this DocumentModel dm) + private static void ValidateMaterials(this DocumentModel dm) { void ValidateDomain(string label, double value, double lowerInclusive, double upperInclusive, int index) { @@ -253,7 +253,7 @@ void ValidateDVector3Domain(string label, DVector3 value, DVector3 lowerInclusiv } } - foreach (var material in dm.MaterialList.ToEnumerable()) + foreach (var material in dm.MaterialList) { var index = material.Index; ValidateDVector3Domain(nameof(material.Color), material.Color, DVector3.Zero, DVector3.One, index); @@ -264,8 +264,8 @@ void ValidateDVector3Domain(string label, DVector3 value, DVector3 lowerInclusiv } } - public static Dictionary> GetViewToElementsMap( - this IArray elementInViewList) + private static Dictionary> GetViewToElementsMap( + this ElementInView[] elementInViewList) => elementInViewList .Select(eiv => (viewIndex: eiv._View?.Index ?? -1, elementIndex: eiv._Element?.Index ?? -1)) .GroupBy(t => t.viewIndex) @@ -273,12 +273,12 @@ public static Dictionary> GetViewToElementsMap( g => g.Key, g => new HashSet(g.Select(t => t.elementIndex))); - public static void ValidateShapesInView(this DocumentModel dm) + private static void ValidateShapesInView(this DocumentModel dm) { - var viewToElementsMap = dm.ElementInViewList.GetViewToElementsMap(); + var viewToElementsMap = dm.ElementInViewList.ToArray().GetViewToElementsMap(); // Validate that the shapes in view have an element which is also in the same view. - foreach (var item in dm.ShapeInViewList.ToEnumerable()) + foreach (var item in dm.ShapeInViewList) { var viewIndex = item._View.Index; var shape = item.Shape; @@ -293,7 +293,7 @@ public static void ValidateShapesInView(this DocumentModel dm) } } - public static void ValidateEntitiesWithElement(this DocumentModel dm) + private static void ValidateEntitiesWithElement(this DocumentModel dm) { var entityWithElementTypes = new HashSet(ObjectModelReflection.GetEntityTypes() .Where(t => t.GetCustomAttributes(typeof(G3dAttributeReferenceAttribute)).Count() == 0)); @@ -302,7 +302,7 @@ public static void ValidateEntitiesWithElement(this DocumentModel dm) Parallel.ForEach(entityWithElementTypes, entityWithElementType => { - var elementIndices = ((IArray)dm.GetPropertyValue(entityWithElementType.Name + "ElementIndex")).ToArray(); + var elementIndices = ((int[])dm.GetPropertyValue(entityWithElementType.Name + "ElementIndex")).ToArray(); for (var i = 0; i < elementIndices.Length; ++i) { var elementIndex = elementIndices[i]; @@ -314,9 +314,9 @@ public static void ValidateEntitiesWithElement(this DocumentModel dm) }); } - public static void ValidateElementInSystem(this DocumentModel dm) + private static void ValidateElementInSystem(this DocumentModel dm) { - foreach (var eis in dm.ElementInSystemList.ToEnumerable()) + foreach (var eis in dm.ElementInSystemList) { if (eis.System == null) throw new ObjectModelValidationException($"{nameof(ElementInSystem)} @ {eis.Index} has a null {nameof(ElementInSystem.System)}"); diff --git a/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs b/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs index 55dc2b94..c0dc5b00 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs @@ -3,12 +3,11 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; -using Vim.BFast; +using Vim.BFastLib; using Vim.Format.Geometry; using Vim.Format.ObjectModel; -using Vim.Util; -using Vim.LinqArray; using Vim.Math3d; +using Vim.Util; namespace Vim.Format.SceneBuilder { @@ -19,28 +18,16 @@ public class VimValidationOptions public static class Validation { - public class VimValidationException : Exception + private class VimValidationException : Exception { public VimValidationException(string message) : base(message) { } } - public static void ValidateGeometry(this VimScene vim) + private static void ValidateDocumentModelToG3dInvariantsNext(this VimScene vim) { - // Validate the packed geometry. - vim.Document.Geometry.ToIMesh().Validate(); - - // Validate the individual meshes. - foreach (var g in vim.Meshes.ToEnumerable()) - g.Validate(); - } - - public static void ValidateDocumentModelToG3dInvariants(this VimScene vim) - { - var g3d = vim._SerializableDocument.Geometry; + var g3d = vim.Document.GeometryNext; var errors = new List(); - errors.AddRange(Vim.G3d.Validation.Validate(g3d).Select(e => e.ToString("G"))); - var entityTypesWithG3dReferences = new HashSet<(Type, G3dAttributeReferenceAttribute[])>( ObjectModelReflection.GetEntityTypes() .Select(t => ( @@ -56,28 +43,27 @@ public static void ValidateDocumentModelToG3dInvariants(this VimScene vim) { var (type, attrs) = tuple; var propertyName = type.Name + "List"; - if (dm.GetPropertyValue(propertyName) is IArray arr) + if (dm.GetPropertyValue(propertyName) is Array arr) { - var numEntities = arr.Count; + var numEntities = arr.Length; foreach (var attr in attrs) { var attributeName = attr.AttributeName; var isOptional = attr.AttributeIsOptional; - var g3dAttribute = g3d.GetAttribute(attributeName); + var count = g3d.CountOf(attributeName); // We don't check the relation if the attribute is optional and absent (null). - if (isOptional && g3dAttribute == null) + if (isOptional && count < 0) continue; - var g3dElementCount = g3dAttribute?.ElementCount ?? 0; var mult = attr.AttributeReferenceMultiplicity; // Validate one-to-one relationships - if (mult == G3dAttributeReferenceMultiplicity.OneToOne && numEntities != g3dElementCount) + if (mult == G3dAttributeReferenceMultiplicity.OneToOne && numEntities != count) { - errors.Add($"Multiplicity Error ({mult}); the number of entities of type \"{type.Name}\" ({numEntities}) is not equal to the number of elements in the g3d attribute \"{attributeName}\" ({g3dElementCount})"); + errors.Add($"Multiplicity Error ({mult}); the number of entities of type \"{type.Name}\" ({numEntities}) is not equal to the number of elements in the g3d attribute \"{attributeName}\" ({count})"); } } } @@ -94,18 +80,17 @@ public static void ValidateDocumentModelToG3dInvariants(this VimScene vim) } } - public static void ValidateNodes(this VimScene vim) + private static void ValidateNodes(this VimScene vim) { - if (vim.VimNodes.Count != vim.DocumentModel.NumNode) - throw new VimValidationException($"The number of {nameof(VimSceneNode)} ({vim.VimNodes.Count}) does not match the number of node entities ({vim.DocumentModel.NumNode})"); + if (vim.GetNodeCount() != vim.DocumentModel.NumNode) + throw new VimValidationException($"The number of {nameof(VimSceneNode)} ({vim.GetNodeCount()}) does not match the number of node entities ({vim.DocumentModel.NumNode})"); } - public static void ValidateShapes(this VimScene vim) + private static void ValidateShapes(this VimScene vim) { - var shapes = vim.VimShapes; - var numShapes = vim.DocumentModel.NumShape; - if (shapes.Count != numShapes) - throw new VimValidationException($"The number of {nameof(VimShape)} ({shapes.Count}) does not match the number of shape entities ({numShapes})"); + var shapes = vim.Shapes; + if (vim.GetShapeCount() != vim.DocumentModel.NumShape) + throw new VimValidationException($"The number of {nameof(VimShapeNext)} ({vim.GetShapeCount()}) does not match the number of shape entities ({vim.DocumentModel.NumShape})"); void ValidateColorDomain(string label, Vector4 value, Vector4 lowerInclusive, Vector4 upperInclusive, int index) { @@ -122,12 +107,13 @@ void ValidateColorDomain(string label, Vector4 value, Vector4 lowerInclusive, Ve } } - Parallel.For(0, numShapes, shapeIndex => + Parallel.For(0, vim.GetShapeCount(), shapeIndex => { var shape = shapes[shapeIndex]; - if (shape.ElementIndex < 0) - throw new VimValidationException($"{nameof(Element)} is null for {nameof(VimShape)} {shape.ShapeIndex}"); - ValidateColorDomain($"{nameof(VimShape)} color", shape.Color, Vector4.Zero, Vector4.One, shape.ShapeIndex); + var element = vim.DocumentModel.GetShapeElementIndex(shapeIndex); + if (element < 0) + throw new VimValidationException($"{nameof(Element)} is null for {nameof(VimShapeNext)} {shape.Index}"); + ValidateColorDomain($"{nameof(VimShapeNext)} color", shape.Color, Vector4.Zero, Vector4.One, shape.Index); }); } @@ -137,31 +123,17 @@ public static void Validate(this VimScene vim, VimValidationOptions options = nu vim.Document.Validate(); vim.DocumentModel.Validate(options.ObjectModelValidationOptions); - vim.ValidateGeometry(); - vim.ValidateDocumentModelToG3dInvariants(); + + VimMesh.FromG3d(vim.Document.GeometryNext).Validate(); + + vim.ValidateDocumentModelToG3dInvariantsNext(); + vim.ValidateNodes(); vim.ValidateShapes(); } public static void ValidateEquality(this DocumentBuilder db, VimScene vim) { - // Test the geometry both ways - var vimGeoBuilders = vim.Meshes.Select(g => new DocumentBuilder.SubdividedMesh( - g.Indices.ToList(), - g.Vertices.ToList(), - g.SubmeshIndexOffsets.ToList(), - g.SubmeshMaterials.ToList() - )).ToList(); - - for (var i = 0; i < db.Meshes.Count; ++i) - { - if (!db.Meshes[i].IsEquivalentTo(vimGeoBuilders[i])) - throw new VimValidationException($"{nameof(DocumentBuilder)} mesh {i} is not equivalent to {nameof(VimScene)} mesh {i}"); - - if (!db.Meshes[i].ToIMesh().GeometryEquals(vim.Meshes[i])) - throw new VimValidationException($"{nameof(DocumentBuilder)} mesh {i} geometry is not equal to {nameof(VimScene)} mesh {i}"); - } - // Test the assets. var dbAssetDictionary = db.Assets; var vimAssetDictionary = vim._SerializableDocument.Assets.ToDictionary(a => a.Name, a => a.ToBytes()); @@ -170,7 +142,7 @@ public static void ValidateEquality(this DocumentBuilder db, VimScene vim) // Test the entity tables. var tableNames = new HashSet(db.Tables.Values.Select(t => t.Name)); - foreach (var et in vim.Document.EntityTables.Keys.ToEnumerable()) + foreach (var et in vim.Document.TableNames) { if (!tableNames.Contains(et)) throw new VimValidationException($"{nameof(DocumentBuilder)} does not contain table name {et} from {nameof(VimScene)}"); diff --git a/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs b/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs index 0d744a39..faa410b4 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs @@ -3,14 +3,13 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using Vim.BFastLib; using Vim.Format; using Vim.Format.Geometry; using Vim.Format.ObjectModel; -using Vim.Util; using Vim.G3d; -using Vim.LinqArray; using Vim.Math3d; - +using Vim.Util; using IVimSceneProgress = System.IProgress<(string, double)>; namespace Vim @@ -20,26 +19,50 @@ namespace Vim /// /// This is the top-level class of a loaded VIM file. /// - public class VimScene : IScene + public class VimScene { + /// + /// Returns the VIM file's header schema version. Returns null if the Vim has no header. + /// + public static SerializableVersion GetSchemaVersion(string path) + { + return GetHeader(path)?.Schema; + } + + /// + /// Returns the VIM file's header. Returns null if the Vim has no header. + /// + public static SerializableHeader GetHeader(string path) + { + return SerializableHeader.FromPath(path); + } + + /// + /// Returns the VIM file's header. Returns null if the Vim has no header. + /// + public static SerializableHeader GetHeader(Stream stream) + { + return SerializableHeader.FromStream(stream); + } + public static VimScene LoadVim(string f, LoadOptions loadOptions, IVimSceneProgress progress = null, bool inParallel = false, int vimIndex = 0) - => new VimScene(Serializer.Deserialize(f, loadOptions), progress, inParallel, vimIndex); + => new VimScene(SerializableDocument.FromPath(f, loadOptions), progress, inParallel, vimIndex); public static VimScene LoadVim(string f, IVimSceneProgress progress = null, bool skipGeometry = false, bool skipAssets = false, bool skipNodes = false, bool inParallel = false) - => LoadVim(f, new LoadOptions { SkipGeometry = skipGeometry, SkipAssets = skipAssets}, progress, inParallel); + => LoadVim(f, new LoadOptions { SkipGeometry = skipGeometry, SkipAssets = skipAssets }, progress, inParallel); public static VimScene LoadVim(Stream stream, LoadOptions loadOptions, IVimSceneProgress progress = null, bool inParallel = false) - => new VimScene(Serializer.Deserialize(stream, loadOptions), progress, inParallel); + => new VimScene(SerializableDocument.FromBFast(new BFast(stream), loadOptions), progress, inParallel); public static VimScene LoadVim(Stream stream, IVimSceneProgress progress = null, bool skipGeometry = false, bool skipAssets = false, bool skipNodes = false, bool inParallel = false) - => LoadVim(stream, new LoadOptions { SkipGeometry = skipGeometry, SkipAssets = skipAssets}, progress, inParallel); + => LoadVim(stream, new LoadOptions { SkipGeometry = skipGeometry, SkipAssets = skipAssets }, progress, inParallel); public int VimIndex { get; set; } - public IArray Meshes { get; private set; } - public IArray Nodes { get; private set; } - public IArray VimNodes { get; private set; } - public IArray VimShapes { get; private set; } - public IArray Materials { get; private set; } + public VimSceneNode[] Nodes { get; private set; } + + public VimMesh[] Meshes { get; private set; } + public VimShapeNext[] Shapes { get; private set; } + public VimMaterialNext[] Materials { get; private set; } public SerializableDocument _SerializableDocument { get; } public Document Document { get; private set; } @@ -48,21 +71,25 @@ public static VimScene LoadVim(Stream stream, IVimSceneProgress progress = null, public string PersistingId => Document.Header.PersistingId; - public Material GetMaterial(int materialIndex) - => DocumentModel.MaterialList.ElementAtOrDefault(materialIndex); + public int GetMeshCount() => Meshes.Length; + public int GetMaterialCount() => Materials.Length; + public int GetShapeCount() => Shapes.Length; + public int GetNodeCount() => Nodes.Length; + + public IEnumerable TransformedMeshes() + => Nodes.Where(n => n.GetMesh() != null).Select(n => n.TransformedMesh()); + + public VimMesh MergedGeometry() + => Nodes.MergedGeometry(); + + public IEnumerable AllVertices() + => TransformedMeshes().SelectMany(g => g.vertices); + + public AABox BoundingBox() + => AABox.Create(AllVertices()); public Vector4 GetMaterialColor(int materialIndex) - => _SerializableDocument.Geometry.MaterialColors[materialIndex]; - - public static IMesh ToIMesh(G3dMesh g3d) - => Primitives.TriMesh( - g3d.Vertices.ToPositionAttribute(), - g3d.Indices.ToIndexAttribute(), - g3d.VertexUvs?.ToVertexUvAttribute(), - g3d.SubmeshIndexOffsets?.ToSubmeshIndexOffsetAttribute(), - g3d.SubmeshMaterials?.ToSubmeshMaterialAttribute(), - g3d.MeshSubmeshOffset?.ToMeshSubmeshOffsetAttribute() - ); + => _SerializableDocument.GeometryNext.MaterialColors[materialIndex]; private VimScene(SerializableDocument doc) => _SerializableDocument = doc; @@ -100,7 +127,7 @@ private Action[] GetInitStepsWithProgress(bool inParallel, IVimSceneProgress pro private IStep[] GetInitSteps(bool inParallel) { var createDocument = new Step( - () => Document = _SerializableDocument.ToDocument(), + () => Document = new Document(_SerializableDocument), "Creating Document" ); @@ -165,54 +192,61 @@ private IStep[] GetInitSteps(bool inParallel) private void CreateMeshes(bool inParallel) { - var srcGeo = _SerializableDocument.Geometry; - var tmp = srcGeo?.Meshes.Select(ToIMesh); - Meshes = (tmp == null) - ? LinqArray.LinqArray.Empty() - : inParallel - ? tmp.EvaluateInParallel() - : tmp.Evaluate(); + if (_SerializableDocument.GeometryNext == null) + { + return; + } + + Meshes = VimMesh.GetAllMeshes(_SerializableDocument.GeometryNext).ToArray(); } private void CreateShapes(bool inParallel) { - var r = _SerializableDocument.Geometry.Shapes.Select((s, i) => new VimShape(this, i)); - VimShapes = inParallel ? r.EvaluateInParallel() : r.Evaluate(); + if (_SerializableDocument.GeometryNext == null) + { + return; + } + Shapes = VimShapeNext.FromG3d(_SerializableDocument.GeometryNext).ToArray(); } private void CreateScene(bool inParallel) { - VimNodes = CreateVimSceneNodes(this, _SerializableDocument.Geometry, inParallel); - Nodes = VimNodes.Select(n => n as ISceneNode); + if (_SerializableDocument.GeometryNext == null) + { + return; + } + + Nodes = CreateVimSceneNodes(this, _SerializableDocument.GeometryNext, inParallel); } private void CreateMaterials(bool inParallel) { - var query = _SerializableDocument.Geometry.Materials.Select(m => new VimMaterial(m) as IMaterial); - Materials = inParallel ? query.EvaluateInParallel() : query.Evaluate(); + if (_SerializableDocument.GeometryNext == null) + { + return; + } + Materials = VimMaterialNext.FromG3d(_SerializableDocument.GeometryNext).ToArray(); } - public static IArray CreateVimSceneNodes(VimScene scene, G3D g3d, bool inParallel) + public static VimSceneNode[] CreateVimSceneNodes(VimScene scene, G3dVim g3d, bool inParallel) { Matrix4x4 GetMatrix(int i) => i >= 0 ? g3d.InstanceTransforms[i] : Matrix4x4.Identity; - - var r = g3d.InstanceTransforms.Select((_, i) => - new VimSceneNode(scene, i, g3d.InstanceMeshes[i], GetMatrix(i))); - return inParallel ? r.EvaluateInParallel() : r.Evaluate(); + return g3d.InstanceTransforms.Select((_, i) => + new VimSceneNode(scene, i, g3d.InstanceMeshes[i], GetMatrix(i))).ToArray(); } public void Save(string filePath) - => _SerializableDocument.Serialize(filePath); + => _SerializableDocument.ToBFast().Write(filePath); public string FileName => _SerializableDocument.FileName; - public void TransformSceneInPlace(Func meshTransform = null, Func nodeTransform = null) + public void TransformSceneInPlace(Func meshTransform = null, Func nodeTransform = null) { if (meshTransform != null) - Meshes = Meshes.Select(meshTransform).EvaluateInParallel(); + Meshes = Meshes.Select(meshTransform).ToArray(); if (nodeTransform != null) - VimNodes = VimNodes.Select(nodeTransform).EvaluateInParallel(); + Nodes = Nodes.Select(nodeTransform).ToArray(); } public string GetElementName(int elementIndex, string missing = "") diff --git a/src/cs/vim/Vim.Format/SceneBuilder/VimSceneHelpers.cs b/src/cs/vim/Vim.Format/SceneBuilder/VimSceneHelpers.cs index 3c6e5ceb..35856380 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/VimSceneHelpers.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/VimSceneHelpers.cs @@ -4,7 +4,6 @@ using Vim.Format; using Vim.Format.ObjectModel; using Vim.Util; -using Vim.LinqArray; using Vim.Math3d; namespace Vim @@ -141,38 +140,27 @@ public static class VimSceneHelpers "Structural:OST_FabricAreas" }; - public static Dictionary CategoryToDiscipline + private static readonly Dictionary CategoryToDiscipline = DisciplineAndCategories.ToDictionary(c => c.Substring(c.IndexOf(':') + 1), c => c.Substring(0, c.IndexOf(':'))); - public static string[] Categories - = CategoryToDiscipline.Keys.OrderBy(x => x).ToArray(); - - public static string GetDisiplineFromCategory(string category, string defaultDiscipline = "Generic") + public static string GetDisciplineFromCategory(string category, string defaultDiscipline = "Generic") => CategoryToDiscipline.GetOrDefault(category ?? "", defaultDiscipline); - - public static Vector4 GetDiffuseColor(this Material m) + + private static Vector4 GetDiffuseColor(this Material m) => m?.Color.ToDiffuseColor(m.Transparency) ?? DefaultColor; - public static Vector4 ToDiffuseColor(this DVector3 v, double transparency) + private static Vector4 ToDiffuseColor(this DVector3 v, double transparency) => new Vector4((float)v.X, (float)v.Y, (float)v.Z, 1.0f - (float)transparency); - public static IArray MaterialColors(this VimScene scene) => scene.DocumentModel.MaterialList.Select(GetDiffuseColor); + public static Vector4[] MaterialColors(this VimScene scene) => scene.DocumentModel.MaterialList.Select(GetDiffuseColor).ToArray(); - public static Vector4 DefaultColor = new Vector4(0.5f, 0.5f, 0.5f, 1); + private static readonly Vector4 DefaultColor = new Vector4(0.5f, 0.5f, 0.5f, 1); public static IEnumerable<(string assetBufferName, FileInfo assetFileInfo)> ExtractAssets(this VimScene vim, DirectoryInfo directory) => vim.Document.ExtractAssets(directory); - public static FileInfo ExtractAsset(this VimScene vim, string assetBufferName, FileInfo fileInfo) - => vim.Document.ExtractAsset(assetBufferName, fileInfo); - - public static ElementInfo GetElementInfo(this VimScene vim, int elementIndex) - => vim.DocumentModel.GetElementInfo(elementIndex); - public static ElementInfo GetElementInfo(this VimScene vim, Element element) => vim.DocumentModel.GetElementInfo(element); - public static VimSchema GetVimSchema(this VimScene vim) - => VimSchema.Create(vim.Document); } } diff --git a/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs b/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs index 5364b04c..300fc45a 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs @@ -1,55 +1,71 @@ -using Vim.Format.Geometry; +using System; +using System.Collections.Generic; +using System.Linq; +using Vim.Format.Geometry; using Vim.Format.ObjectModel; using Vim.G3d; -using Vim.LinqArray; using Vim.Math3d; namespace Vim { - public sealed class VimSceneNode : ElementInfo, ISceneNode, ITransformable3D + public sealed class VimSceneNode : ElementInfo, ITransformable3D { public VimSceneNode(VimScene scene, int nodeIndex, int geometryIndex, Matrix4x4 transform) : base(scene.DocumentModel, scene.DocumentModel.GetNodeElementIndex(nodeIndex)) { VimIndex = scene.VimIndex; - _Scene = scene; + Scene = scene; Transform = transform; MeshIndex = geometryIndex; NodeIndex = nodeIndex; } - public VimScene _Scene { get; } + public VimScene Scene { get; } - public IScene Scene => _Scene; public int Id => NodeIndex; public Matrix4x4 Transform { get; } public InstanceFlags InstanceFlags - => (InstanceFlags)_Scene.Document.Geometry.InstanceFlags.ElementAtOrDefault(NodeIndex); + => (InstanceFlags) Scene.Document.GeometryNext.InstanceFlags.ElementAtOrDefault(NodeIndex); public bool HideByDefault - => (InstanceFlags & InstanceFlags.Hidden) == InstanceFlags.Hidden; + => Scene.Document.GeometryNext.InstanceHasFlag(NodeIndex, InstanceFlags.Hidden); public int VimIndex { get; } = -1; public int NodeIndex { get; } = -1; - public IMesh GetMesh() - => _Scene.Meshes.ElementAtOrDefault(MeshIndex); + public VimMesh GetMesh() + => Scene.Meshes.SafeGet(MeshIndex); public int MeshIndex { get; } public bool HasMesh => MeshIndex != -1; - public Node NodeModel => _Scene.DocumentModel.GetNode(Id); - public Geometry GeometryModel => _Scene.DocumentModel.GetGeometry(MeshIndex); + public Node NodeModel => Scene.DocumentModel.GetNode(Id); + public Geometry GeometryModel => Scene.DocumentModel.GetGeometry(MeshIndex); // TODO: I think this should be "IEnumerable" in the interface - public ISceneNode Parent => null; - public IArray Children => LinqArray.LinqArray.Empty(); + public VimSceneNode Parent => null; + public VimSceneNode[] Children => Array.Empty(); - public string DisciplineName => VimSceneHelpers.GetDisiplineFromCategory(CategoryName); + public string DisciplineName => VimSceneHelpers.GetDisciplineFromCategory(CategoryName); VimSceneNode ITransformable3D.Transform(Matrix4x4 mat) - => new VimSceneNode(_Scene, Id, MeshIndex, mat * Transform); + => new VimSceneNode(Scene, Id, MeshIndex, mat * Transform); + + public VimMesh TransformedMesh() + => GetMesh()?.Transform(Transform); + + public Vector3[] TransformedVertices() + => TransformedMesh()?.vertices; + + public AABox TransformedBoundingBox() + => AABox.Create(TransformedVertices()); + } + + public static class NodeExtensions + { + public static VimMesh MergedGeometry(this IEnumerable nodes) + => nodes.Where(n => n.GetMesh() != null).Select(n => n.TransformedMesh()).ToArray().Merge(); } } diff --git a/src/cs/vim/Vim.Format/SceneBuilder/VimShape.cs b/src/cs/vim/Vim.Format/SceneBuilder/VimShape.cs deleted file mode 100644 index 112a6a13..00000000 --- a/src/cs/vim/Vim.Format/SceneBuilder/VimShape.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Vim.Format.ObjectModel; -using Vim.G3d; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim -{ - public class VimShape : ElementInfo - { - public readonly VimScene Scene; - public readonly int ShapeIndex; - - public G3dShape G3dShape => Scene.Document.Geometry.Shapes[ShapeIndex]; - public IArray Vertices => G3dShape.Vertices; - public Vector4 Color => G3dShape.Color; - public float Width => G3dShape.Width; - - public VimShape(VimScene scene, int shapeIndex) - : base(scene.DocumentModel, scene.DocumentModel.GetShapeElementIndex(shapeIndex)) - { - Scene = scene; - ShapeIndex = shapeIndex; - } - } -} diff --git a/src/cs/vim/Vim.Format/Vim.Format.csproj b/src/cs/vim/Vim.Format/Vim.Format.csproj index 5f258aac..db3e19f8 100644 --- a/src/cs/vim/Vim.Format/Vim.Format.csproj +++ b/src/cs/vim/Vim.Format/Vim.Format.csproj @@ -10,7 +10,7 @@ GitHub true MIT - 1.1.0 + 1.0.2 true true true @@ -18,12 +18,9 @@ - - - - - - + + +