diff --git a/license.txt b/LICENSE.txt similarity index 100% rename from license.txt rename to LICENSE.txt diff --git a/docs/bfast.md b/docs/bfast.md index caaa6cec..318a6450 100644 --- a/docs/bfast.md +++ b/docs/bfast.md @@ -31,7 +31,6 @@ containers for other data. * 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 @@ -68,7 +67,7 @@ The file format consists of three sections: * Header - Fixed size descriptor (32 bytes) describing the file contents * Ranges - An array of offset pairs indicating the begin and end of each buffer (relative to file begin) -* Data - 64-byte aligned data buffers +* Data - Data buffers ## Header Section @@ -103,7 +102,7 @@ offsets relative to the beginning of the file. ## Data Section -The data section starts at the first 64 byte aligned address immediately following the last `Range` value. +The data section starts just after the last `Range` value. This value is stored for validation purposes in the header as `DataStart`. ### Names Buffer @@ -113,12 +112,3 @@ strings separated by null characters. Names may be zero-length and are not guara A name may contain any Utf-8 encoded character except the null character. There must be N-1 names where N is the number of ranges (i.e. the `NumArrays` value in header). - -# Implementations - -The official reference implementation of BFAST is written in C# and targets .NET Standard 2.0. The C# -test suite uses NUnit and targets .NET Core 2.1. At VIM AEC we are using BFAST in production code that -targets Unity 2019.1 and .NET Framework 4.7.1. - -There is currently a C++ encoder and a JavaScript decoder implementation under development, but they -are not tested and supported yet. \ No newline at end of file diff --git a/src/cs/.editorconfig b/src/cs/.editorconfig new file mode 100644 index 00000000..7f629237 --- /dev/null +++ b/src/cs/.editorconfig @@ -0,0 +1,215 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +# Default severity for analyzer diagnostics with category 'Style' (escalated to build warnings) +# This causes the build to fail for the cases below that are suffixed with :error +dotnet_analyzer_diagnostic.category-Style.severity = default + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = true + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false:error +dotnet_style_qualification_for_field = false:error +dotnet_style_qualification_for_method = false:error +dotnet_style_qualification_for_property = false:error + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true:error +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = true:error +csharp_style_var_for_built_in_types = true:error +csharp_style_var_when_type_is_apparent = true:error + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = true:silent +csharp_style_expression_bodied_methods = true:silent +csharp_style_expression_bodied_operators = true:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null-checking preferences +csharp_style_conditional_delegate_call = true + +# Modifier preferences +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async + +# Code-block preferences +csharp_prefer_braces = when_multiline:error +csharp_prefer_simple_using_statement = true + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_pattern_local_over_anonymous_function = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_range_operator = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +# Web development files use 2 spaces +[*.{js,jsx,ts,tsx,py,css,json}] +indent_size = 2 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..fb2ee1a5 --- /dev/null +++ b/src/cs/bfast/Vim.BFast.Tests/BFastTests.cs @@ -0,0 +1,577 @@ +using NUnit.Framework; +using NUnit.Framework.Constraints; +using System.Data; +using Vim.BFastLib.Core; +using Vim.Util.Tests; + +namespace Vim.BFastLib.Tests +{ + [TestFixture] + 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 97% rename from src/cs/bfast/Vim.BFast/BufferExtensions.cs rename to src/cs/bfast/Vim.BFast/Buffers/BufferExtensions.cs index d07b8eb5..6260c22b 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(); 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..c23b45e0 100644 --- a/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs +++ b/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs @@ -1,377 +1,81 @@ -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); - } - } - [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(TestUtils.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/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..78ab447b --- /dev/null +++ b/src/cs/g3d/Vim.G3d/G3dVim.cs @@ -0,0 +1,227 @@ +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; + 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(); + _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 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/linqarray/Vim.LinqArray.Tests/LinqArrayTests.cs b/src/cs/linqarray/Vim.LinqArray.Tests/LinqArrayTests.cs deleted file mode 100644 index 4f85ca04..00000000 --- a/src/cs/linqarray/Vim.LinqArray.Tests/LinqArrayTests.cs +++ /dev/null @@ -1,72 +0,0 @@ -using NUnit.Framework; -using System; - -namespace Vim.LinqArray.Tests -{ - public class LinqArrayTests - { - public static int[] ArrayToTen = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - public static IArray RangeToTen = 10.Range(); - public static IArray BuildToTen = LinqArray.Build(0, x => x + 1, x => x < 10); - - public static object[] TensData = { ArrayToTen.ToIArray(), RangeToTen, BuildToTen }; - - [TestCaseSource(nameof(TensData))] - public void CheckTens(IArray tens) - { - Assert.IsTrue(tens.SequenceEquals(ArrayToTen.ToIArray())); - Assert.AreEqual(0, tens.First()); - Assert.AreEqual(9, tens.Last()); - Assert.AreEqual(45, tens.Aggregate(0, (a, b) => a + b)); - Assert.AreEqual(10, tens.Count); - Assert.AreEqual(5, tens[5]); - Assert.AreEqual(5, tens.ElementAt(5)); - - var ones = 1.Repeat(9); - var diffs = tens.ZipEachWithNext((x, y) => y - x); - Assert.IsTrue(ones.SequenceEquals(diffs)); - Assert.IsFalse(ones.SequenceEquals(tens)); - - var indices = tens.Indices(); - Assert.IsTrue(tens.SequenceEquals(indices)); - Assert.IsTrue(tens.SequenceEquals(tens.SelectByIndex(indices))); - Assert.IsTrue(tens.Reverse().SequenceEquals(tens.SelectByIndex(indices.Reverse()))); - - var sum = 0; - foreach (var x in tens.ToEnumerable()) - { - sum += x; - } - foreach (var x in tens.ToEnumerable()) - { - Console.WriteLine(x.ToString()); - } - Assert.AreEqual(45, sum); - Assert.AreEqual(0, tens.First()); - Assert.True(tens.All(x => x < 10)); - Assert.True(tens.Any(x => x < 5)); - Assert.AreEqual(5, tens.CountWhere(x => x % 2 == 0)); - Assert.AreEqual(0, tens.Reverse().Last()); - Assert.AreEqual(0, tens.Reverse().Reverse().First()); - var split = tens.Split(LinqArray.Create(3, 6)); - Assert.AreEqual(3, split.Count); - - var batch = tens.SubArrays(3); - Assert.AreEqual(4, batch.Count); - Assert.True(batch[0].SequenceEquals(LinqArray.Create(0, 1, 2))); - Assert.True(batch[3].SequenceEquals(LinqArray.Create(9))); - - var batch2 = tens.Take(9).SubArrays(3); - Assert.AreEqual(3, batch2.Count); - - var counts = split.Select(x => x.Count); - Assert.True(counts.SequenceEquals(LinqArray.Create(3, 3, 4))); - var indices2 = counts.Accumulate((x, y) => x + y); - Assert.True(indices2.SequenceEquals(LinqArray.Create(3, 6, 10))); - var indices3 = counts.PostAccumulate((x, y) => x + y); - Assert.True(indices3.SequenceEquals(LinqArray.Create(0, 3, 6, 10))); - var flattened = split.Flatten(); - Assert.True(flattened.SequenceEquals(tens)); - } - } -} diff --git a/src/cs/linqarray/Vim.LinqArray.Tests/Vim.LinqArray.Tests.csproj b/src/cs/linqarray/Vim.LinqArray.Tests/Vim.LinqArray.Tests.csproj index fb957c18..38321c15 100644 --- a/src/cs/linqarray/Vim.LinqArray.Tests/Vim.LinqArray.Tests.csproj +++ b/src/cs/linqarray/Vim.LinqArray.Tests/Vim.LinqArray.Tests.csproj @@ -12,7 +12,13 @@ - + + + + + True + + diff --git a/src/cs/linqarray/Vim.LinqArray/ILookup.cs b/src/cs/linqarray/Vim.LinqArray/ILookup.cs deleted file mode 100644 index 4bf6dc81..00000000 --- a/src/cs/linqarray/Vim.LinqArray/ILookup.cs +++ /dev/null @@ -1,78 +0,0 @@ -// MIT License - Copyright 2019 (C) VIMaec, LLC. -// MIT License - Copyright 2018 (C) Ara 3D, Inc. -// This file is subject to the terms and conditions defined in -// file 'LICENSE.txt', which is part of this source code package. - -using System.Collections.Generic; -using System.Linq; - -namespace Vim.LinqArray -{ - /// - /// Lookup table: mapping from a key to some value. - /// - public interface ILookup - { - IArray Keys { get; } - IArray Values { get; } - bool Contains(TKey key); - TValue this[TKey key] { get; } - } - - public class EmptyLookup : ILookup - { - public IArray Keys => LinqArray.Empty(); - public IArray Values => LinqArray.Empty(); - public bool Contains(TKey key) => false; - public TValue this[TKey key] => default; - } - - public class LookupFromDictionary : ILookup - { - public IDictionary Dictionary; - private TValue _default; - - public LookupFromDictionary(IDictionary d = null, TValue defaultValue = default) - { - Dictionary = d ?? new Dictionary(); - // TODO: sort? - _default = defaultValue; - Keys = d.Keys.ToIArray(); - Values = d.Values.ToIArray(); - } - - public IArray Keys { get; } - public IArray Values { get; } - public TValue this[TKey key] => Contains(key) ? Dictionary[key] : _default; - public bool Contains(TKey key) => Dictionary.ContainsKey(key); - } - - public class LookupFromArray : ILookup - { - private IArray array; - - public LookupFromArray(IArray xs) - { - array = xs; - Keys = array.Indices(); - Values = array; - } - - public IArray Keys { get; } - public IArray Values { get; } - public TValue this[int key] => array[key]; - public bool Contains(int key) => key >= 0 && key <= array.Count; - } - - public static class LookupExtensions - { - public static ILookup ToLookup(this IDictionary d, TValue defaultValue = default) - => new LookupFromDictionary(d, defaultValue); - - public static TValue GetOrDefault(this ILookup lookup, TKey key) - => lookup.Contains(key) ? lookup[key] : default; - - public static IEnumerable GetValues(this ILookup lookup) - => lookup.Keys.ToEnumerable().Select(k => lookup[k]); - } -} diff --git a/src/cs/linqarray/Vim.LinqArray/LinqArray.cs b/src/cs/linqarray/Vim.LinqArray/LinqArray.cs deleted file mode 100644 index 92cc3b45..00000000 --- a/src/cs/linqarray/Vim.LinqArray/LinqArray.cs +++ /dev/null @@ -1,1016 +0,0 @@ -// MIT License - Copyright 2019 (C) VIMaec, LLC. -// MIT License - Copyright (C) Ara 3D, Inc. -// This file is subject to the terms and conditions defined in -// file 'LICENSE.txt', which is part of this source code package. -// -// LinqArray.cs -// A library for working with pure functional arrays, using LINQ style extension functions. -// Based on code that originally appeared in https://www.codeproject.com/Articles/140138/Immutable-Array-for-NET - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Vim.LinqArray -{ - /// - /// Represents an immutable array with expected O(1) complexity when - /// retrieving the number of items. - /// - public interface IArray - { - int Count { get; } - } - - /// - /// Represents an immutable array with expected O(1) complexity when - /// retrieving the number of items and random element access. - /// - public interface IArray : IArray - { - T this[int n] { get; } - } - - /// - /// Implements an IArray via a function and a count. - /// - public class FunctionalArray : IArray - { - public readonly Func Function; - - public int Count - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get; - } - - public T this[int n] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Function(n); - } - public FunctionalArray(int count, Func function) - => (Count, Function) = (count, function); - } - - /// - /// Implements an IArray from a System.Array. - /// - public class ArrayAdapter : IArray - { - public readonly T[] Array; - - public int Count - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get; - } - - public T this[int n] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Array[n]; - } - public ArrayAdapter(T[] xs) { Count = xs.Length; Array = xs; } - } - - /// - /// Extension functions for working on any object implementing IArray. This overrides - /// many of the Linq functions providing better performance. - /// - public static class LinqArray - { - /// - /// Helper function for creating an IArray from the arguments. - /// - public static IArray Create(params T[] self) - => self.ToIArray(); - - /// - /// A helper function to enable IArray to support IEnumerable - /// - public static IEnumerable ToEnumerable(this IArray self) - => Enumerable.Range(0, self.Count).Select(self.ElementAt); - - public static IArray ForEach(this IArray xs, Action f) - { - for (var i = 0; i < xs.Count; ++i) - f(xs[i]); - return xs; - } - - /// - /// Creates an IArray with the given number of items, - /// and uses the function to return items. - /// - public static IArray Select(this int count, Func f) - => new FunctionalArray(count, f); - - /// - /// Converts any implementation of IList (e.g. Array/List) to an IArray. - /// - public static IArray ToIArray(this IList self) - => self.Count.Select(i => self[i]); - - /// - /// Converts any implementation of IList (e.g. Array/List) to an IArray. - /// - public static IArray ToIArray(this T[] self) - => new ArrayAdapter(self); - - /// - /// Converts any implementation of IEnumerable to an IArray - /// - public static IArray ToIArray(this IEnumerable self) - => self is IList xs ? xs.ToIArray() : self.ToArray().ToIArray(); - - /// - /// Creates an IArray by repeating the given item a number of times. - /// - public static IArray Repeat(this T self, int count) - => Select(count, i => self); - - /// - /// Creates an IArray by repeating each item in the source a number of times. - /// - public static IArray RepeatElements(this IArray self, int count) - => Select(count, i => self[i / count]); - - /// - /// Creates an IArray starting with a seed value, and applying a function - /// to each individual member of the array. This is eagerly evaluated. - /// - public static IArray Generate(this T init, int count, Func f) - { - var r = new T[count]; - for (var i = 0; i < count; ++i) - { - r[i] = init; - init = f(init); - } - return r.ToIArray(); - } - - /// - /// Creates an IArray of integers from zero up to one less than the given number. - /// - public static IArray Range(this int self) - => Select(self, i => i); - - /// - /// Returns the first item in the array. - /// - public static T First(this IArray self, T @default = default) - => self.IsEmpty() ? @default : self[0]; - - /// - /// Returns the last item in the array - /// - public static T Last(this IArray self, T @default = default) - => self.IsEmpty() ? @default : self[self.Count - 1]; - - /// - /// Returns true if and only if the argument is a valid index into the array. - /// - public static bool InRange(this IArray self, int n) - => n >= 0 && n < self.Count; - - /// - /// A mnemonic for "Any()" that returns false if the count is greater than zero - /// - public static bool IsEmpty(this IArray self) - => !self.Any(); - - /// - /// Returns true if there are any elements in the array. - /// - public static bool Any(this IArray self) - => self.Count != 0; - - /// - /// Converts the IArray into a system array. - /// - public static T[] ToArray(this IArray self) - => self.CopyTo(new T[self.Count]); - - /// - /// Converts the IArray into a system List. - /// - public static List ToList(this IArray self) - => self.ToEnumerable().ToList(); - - /// - /// Converts the array into a function that returns values from an integer, returning a default value if out of range. - /// - public static Func ToFunction(this IArray self, T def = default) - => i => self.InRange(i) ? self[i] : def; - - /// - /// Converts the array into a predicate (a function that returns true or false) based on the truth of the given value. - /// - public static Func ToPredicate(this IArray self) - => self.ToFunction(); - - /// - /// Adds all elements of the array to the target collection. - /// - public static U AddTo(this IArray self, U other) where U : ICollection - { - self.ForEach(other.Add); - return other; - } - - /// - /// Copies all elements of the array to the target list or array, starting at the provided index. - /// - public static U CopyTo(this IArray self, U other, int destIndex = 0) where U : IList - { - for (var i = 0; i < self.Count; ++i) - other[i + destIndex] = self[i]; - return other; - } - - /// - /// Returns an array generated by applying a function to each element. - /// - public static IArray Select(this IArray self, Func f) - => Select(self.Count, i => f(self[i])); - - /// - /// Returns an array generated by applying a function to each element. - /// - public static IArray Select(this IArray self, Func f) - => Select(self.Count, i => f(self[i], i)); - - /// - /// Returns an array generated by applying a function to each element. - /// - public static IArray SelectIndices(this IArray self, Func f) - => self.Count.Select(f); - - /// - /// Converts an array of array into a flattened array. Each array is assumed to be of size n. - /// - public static IArray Flatten(this IArray> self, int n) - => Select(self.Count * n, i => self[i / n][i % n]); - - /// - /// Converts an array of array into a flattened array. - /// - public static IArray Flatten(this IArray> self) - { - var counts = self.Select(x => x.Count).PostAccumulate((x, y) => x + y); - var r = new T[counts.Last()]; - var i = 0; - foreach (var xs in self.ToEnumerable()) - xs.CopyTo(r, counts[i++]); - return r.ToIArray(); - } - - /// - /// Returns an array of tuple where each element of the initial array is paired with its index. - /// - public static IArray<(T value, int index)> ZipWithIndex(this IArray self) - => self.Select((v, i) => (v, i)); - - /// - /// Returns an array from an array of arrays, where the number of sub-elements is the same for reach array and is known. - /// - public static IArray SelectMany(this IArray> self, int count) - => Select(self.Count, i => self[i / count][i % count]); - - /// - /// Returns an array given a function that generates an IArray from each member. Eager evaluation. - /// - public static IArray SelectMany(this IArray self, Func> func) - { - var xs = new List(); - for (var i = 0; i < self.Count; ++i) - func(self[i]).AddTo(xs); - return xs.ToIArray(); - } - - /// - /// Returns an array given a function that generates an IArray from each member. Eager evaluation. - /// - public static IArray SelectMany(this IArray self, Func> func) - { - var xs = new List(); - for (var i = 0; i < self.Count; ++i) - func(self[i], i).AddTo(xs); - return xs.ToIArray(); - } - - /// - /// Returns an array given a function that generates a tuple from each member. Eager evaluation. - /// - public static IArray SelectMany(this IArray self, Func> func) - { - var r = new U[self.Count * 2]; - for (var i = 0; i < self.Count; ++i) - { - var tmp = func(self[i]); - r[i * 2] = tmp.Item1; - r[i * 2 + 1] = tmp.Item2; - } - - return r.ToIArray(); - } - - /// - /// Returns an array given a function that generates a tuple from each member. Eager evaluation. - /// - public static IArray SelectMany(this IArray self, Func> func) - { - var r = new U[self.Count * 3]; - for (var i = 0; i < self.Count; ++i) - { - var tmp = func(self[i]); - r[i * 3] = tmp.Item1; - r[i * 3 + 1] = tmp.Item2; - r[i * 3 + 2] = tmp.Item3; - } - return r.ToIArray(); - } - - /// - /// Returns an array given a function that generates a tuple from each member. Eager evaluation. - /// - public static IArray SelectMany(this IArray self, Func> func) - { - var r = new U[self.Count * 4]; - for (var i = 0; i < self.Count; ++i) - { - var tmp = func(self[i]); - r[i * 4] = tmp.Item1; - r[i * 4 + 1] = tmp.Item2; - r[i * 4 + 2] = tmp.Item3; - r[i * 4 + 3] = tmp.Item4; - } - return r.ToIArray(); - } - - /// - /// Returns an array generated by applying a function to corresponding pairs of elements in both arrays. - /// - public static IArray Zip(this IArray self, IArray other, Func f) - => Select(Math.Min(self.Count, other.Count), i => f(self[i], other[i])); - - /// - /// Returns an array generated by applying a function to corresponding pairs of elements in both arrays. - /// - public static IArray Zip(this IArray self, IArray other, Func f) - => Select(Math.Min(self.Count, other.Count), i => f(self[i], other[i], i)); - - /// - /// Returns an array generated by applying a function to corresponding pairs of elements in both arrays. - /// - public static IArray Zip(this IArray self, IArray other, IArray other2, Func f) - => Select(Math.Min(Math.Min(self.Count, other.Count), other2.Count), i => f(self[i], other[i], other2[i])); - - /// - /// Returns an array generated by applying a function to corresponding pairs of elements in both arrays. - /// - public static IArray Zip(this IArray self, IArray other, IArray other2, Func f) - => Select(Math.Min(Math.Min(self.Count, other.Count), other2.Count), i => f(self[i], other[i], other2[i], i)); - - /// - /// Returns an array generated by applying a function to corresponding pairs of elements in both arrays. - /// - public static IArray Zip(this IArray self, IArray other, IArray other2, IArray other3, Func f) - => Select(Math.Min(Math.Min(self.Count, other.Count), Math.Min(other2.Count, other3.Count)), i => f(self[i], other[i], other2[i], other3[i])); - - /// - /// Returns an array generated by applying a function to corresponding pairs of elements in both arrays. - /// - public static IArray Zip(this IArray self, IArray other, IArray other2, IArray other3, Func f) - => Select(Math.Min(Math.Min(self.Count, other.Count), Math.Min(other2.Count, other3.Count)), i => f(self[i], other[i], other2[i], other3[i], i)); - - /// - /// Applies a function to each element in the list paired with the next one. - /// Used to implement adjacent differences for example. - /// - public static IArray ZipEachWithNext(this IArray self, Func f) - => self.Zip(self.Skip(), f); - - /// - /// Returns an IEnumerable containing only elements of the array for which the function returns true on the index. - /// An IArray is not created automatically because it is an expensive operation that is potentially unneeded. - /// - public static IEnumerable WhereIndices(this IArray self, Func f) - => self.Where((x, i) => f(i)); - - /// - /// Returns an IEnumerable containing only elements of the array for which the corresponding mask is true. - /// An IArray is not created automatically because it is an expensive operation that is potentially unneeded. - /// - public static IEnumerable Where(this IArray self, IArray mask) - => self.WhereIndices(mask.ToPredicate()); - - /// - /// Returns an IEnumerable containing only elements of the array for which the corresponding predicate is true. - /// - public static IEnumerable Where(this IArray self, Func predicate) - => self.ToEnumerable().Where(predicate); - - /// - /// Returns an IEnumerable containing only elements of the array for which the corresponding predicate is true. - /// - public static IEnumerable Where(this IArray self, Func predicate) - => self.ToEnumerable().Where(predicate); - - /// - /// Returns an IEnumerable containing only indices of the array for which the function satisfies a specific predicate. - /// An IArray is not created automatically because it is an expensive operation that is potentially unneeded. - /// - public static IEnumerable IndicesWhere(this IArray self, Func f) - => self.Indices().Where(i => f(self[i])); - - /// - /// Returns an IEnumerable containing only indices of the array for which the function satisfies a specific predicate. - /// An IArray is not created automatically because it is an expensive operation that is potentially unneeded. - /// - public static IEnumerable IndicesWhere(this IArray self, Func f) - => self.IndicesWhere(i => f(self[i], i)); - - /// - /// Returns an IEnumerable containing only indices of the array for which the function satisfies a specific predicate. - /// An IArray is not created automatically because it is an expensive operation that is potentially unneeded. - /// - public static IEnumerable IndicesWhere(this IArray self, Func f) - => self.Indices().Where(i => f(i)); - - /// - /// Returns an IEnumerable containing only indices of the array for which booleans in the mask are true. - /// An IArray is not created automatically because it is an expensive operation that is potentially unneeded. - /// - public static IEnumerable IndicesWhere(this IArray self, IArray mask) - => self.IndicesWhere(mask.ToPredicate()); - - /// - /// Shortcut for ToEnumerable.Aggregate() - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static U Aggregate(this IArray self, U init, Func func) - { - for (var i = 0; i < self.Count; ++i) - init = func(init, self[i]); - return init; - } - - /// - /// Shortcut for ToEnumerable.Aggregate() - /// - public static U Aggregate(this IArray self, Func func) - => Aggregate(self, default, func); - - /// - /// Shortcut for ToEnumerable.Aggregate() - /// - public static U Aggregate(this IArray self, U init, Func func) - { - for (var i = 0; i < self.Count; ++i) - init = func(init, self[i], i); - return init; - } - - /// - /// Shortcut for ToEnumerable.Aggregate() - /// - public static U Aggregate(this IArray self, Func func) - => Aggregate(self, default, func); - - /// - /// Returns a new array containing the elements in the range of from to to. - /// - public static IArray Slice(this IArray self, int from, int to) - => Select(to - from, i => self[i + from]); - - /// - /// Returns an array of SubArrays of size "size" - /// the last items that cannot fill an arrat if size "size" will be ignored - /// - public static IArray> SubArraysFixed(this IArray self, int size) - => (self.Count / size).Select(i => self.SubArray(i, size)); - - - /// Returns an array of SubArrays of size "size" plus extras - /// The extra array is of size count % size if present - public static IArray> SubArrays(this IArray self, int size) - => self.Count % size == 0 - ? self.SubArraysFixed(size) - : self.SubArraysFixed(size).Append(self.TakeLast(self.Count % size)); - - /// - /// Returns n elements of the list starting from a given index. - /// - public static IArray SubArray(this IArray self, int from, int count) - => self.Slice(from, count + from); - - /// - /// Returns elements of the array between from and skipping every stride element. - /// - public static IArray Slice(this IArray self, int from, int to, int stride) - => Select(to - from / stride, i => self[i * stride + from]); - - /// - /// Returns a new array containing the elements by taking every nth item. - /// - public static IArray Stride(this IArray self, int n) - => Select(self.Count / n, i => self[i * n % self.Count]); - - /// - /// Returns a new array containing just the first n items. - /// - public static IArray Take(this IArray self, int n) - => self.Slice(0, n); - - /// - /// Returns a new array containing just at most n items. - /// - public static IArray TakeAtMost(this IArray self, int n) - => self.Count > n ? self.Slice(0, n) : self; - - /// - /// Returns a new array containing the elements after the first n elements. - /// - public static IArray Skip(this IArray self, int n = 1) - => self.Slice(n, self.Count); - - /// - /// Returns a new array containing the last n elements. - /// - public static IArray TakeLast(this IArray self, int n = 1) - => self.Skip(self.Count - n); - - /// - /// Returns a new array containing all elements excluding the last n elements. - /// - public static IArray DropLast(this IArray self, int n = 1) - => self.Count > n ? self.Take(self.Count - n) : self.Empty(); - - /// - /// Returns a new array by remapping indices - /// - public static IArray MapIndices(this IArray self, Func f) - => self.Count.Select(i => self[f(i)]); - - /// - /// Returns a new array that reverses the order of elements - /// - public static IArray Reverse(this IArray self) - => self.MapIndices(i => self.Count - 1 - i); - - /// - /// Uses the provided indices to select elements from the array. - /// - public static IArray SelectByIndex(this IArray self, IArray indices) - => indices.Select(i => self[i]); - - /// - /// Uses the array as indices to select elements from the other array. - /// - public static IArray Choose(this IArray indices, IArray values) - => values.SelectByIndex(indices); - - /// - /// Given indices of sub-arrays groups, this will convert it to arrays of indices (e.g. [0, 2] with a group size of 3 becomes [0, 1, 2, 6, 7, 8]) - /// - public static IArray GroupIndicesToIndices(this IArray indices, int groupSize) - => groupSize == 1 - ? indices : (indices.Count * groupSize).Select(i => indices[i / groupSize] * groupSize + i % groupSize); - - /// - /// Return the array separated into a series of groups (similar to DictionaryOfLists) - /// based on keys created by the given keySelector - /// - public static IEnumerable> GroupBy(this IArray self, Func keySelector) - => self.ToEnumerable().GroupBy(keySelector); - - /// - /// Return the array separated into a series of groups (similar to DictionaryOfLists) - /// based on keys created by the given keySelector and elements chosen by the element selector - /// - public static IEnumerable> GroupBy(this IArray self, Func keySelector, Func elementSelector) - => self.ToEnumerable().GroupBy(keySelector, elementSelector); - - /// - /// Uses the provided indices to select groups of contiguous elements from the array. - /// This is equivalent to self.SubArrays(groupSize).SelectByIndex(indices).SelectMany(); - /// - public static IArray SelectGroupsByIndex(this IArray self, int groupSize, IArray indices) - => self.SelectByIndex(indices.GroupIndicesToIndices(groupSize)); - - /// - /// Similar to take, if count is less than the number of items in the array, otherwise uses a modulo operation. - /// - public static IArray Resize(this IArray self, int count) - => Select(count, i => self[i % self.Count]); - - /// - /// Returns an array of the same type with no elements. - /// - public static IArray Empty(this IArray self) - => self.Take(0); - - /// - /// Returns an array of the same type with no elements. - /// - public static IArray Empty() - => default(T).Repeat(0); - - /// - /// Returns a sequence of integers from 0 to 1 less than the number of items in the array, representing indicies of the array. - /// - public static IArray Indices(this IArray self) - => self.Count.Range(); - - /// - /// Converts an array of elements into a string representation - /// - public static string Join(this IArray self, string sep = " ") - => self.Aggregate(new StringBuilder(), (sb, x) => sb.Append(x).Append(sep)).ToString(); - - /// - /// Concatenates the contents of one array with another. - /// - public static IArray Concatenate(this IArray self, IArray other) - => Select(self.Count + other.Count, i => i < self.Count ? self[i] : other[i - self.Count]); - - /// - /// Returns the index of the first element matching the given item. - /// - public static int IndexOf(this IArray self, T item) where T : IEquatable - => self.IndexOf(x => x.Equals(item)); - - /// - /// Returns the index of the first element matching the given item. - /// - public static int IndexOf(this IArray self, Func predicate) - { - for (var i = 0; i < self.Count; ++i) - { - if (predicate(self[i])) - return i; - } - - return -1; - } - - /// - /// Returns the index of the last element matching the given item. - /// - public static int LastIndexOf(this IArray self, T item) where T : IEquatable - { - var n = self.Reverse().IndexOf(item); - return n < 0 ? n : self.Count - 1 - n; - } - - /// - /// Returns an array that is one element shorter that subtracts each element from its previous one. - /// - public static IArray AdjacentDifferences(this IArray self) - => self.ZipEachWithNext((a, b) => b - a); - - /// - /// Creates a new array that concatenates a unit item list of one item after it. - /// Repeatedly calling Append would result in significant performance degradation. - /// - public static IArray Append(this IArray self, T x) - => (self.Count + 1).Select(i => i < self.Count ? self[i] : x); - - /// - /// Creates a new array that concatenates the given items to itself. - /// - public static IArray Append(this IArray self, params T[] x) - => self.Concatenate(x.ToIArray()); - - /// - /// 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 IArray Prepend(this IArray self, T x) - => (self.Count + 1).Select(i => i == 0 ? x : self[i - 1]); - - /// - /// Returns the element at the nth position, where n is modulo the number of items in the arrays. - /// - public static T ElementAt(this IArray self, int n) - => self[n]; - - /// - /// Returns the element at the nth position, where n is modulo the number of items in the arrays. - /// - public static T ElementAtModulo(this IArray self, int n) - => self.ElementAt(n % self.Count); - - /// - /// Returns the Nth element of the array, or a default value if out of range/ - /// - public static T ElementAtOrDefault(this IArray xs, int n, T defaultValue = default) - => xs != null && n >= 0 && n < xs.Count ? xs[n] : defaultValue; - - /// - /// Counts all elements in an array that satisfy a predicate - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CountWhere(this IArray self, Func p) - => self.Aggregate(0, (n, x) => n + (p(x) ? 1 : 0)); - - /// - /// Counts all elements in an array that are equal to true - /// - public static int CountWhere(this IArray self) - => self.CountWhere(x => x); - - /// - /// Counts all elements in an array that are equal to a value - /// - public static int CountWhere(this IArray self, T val) where T : IEquatable - => self.CountWhere(x => x.Equals(val)); - - /// - /// Returns the minimum element in the list - /// - public static T Min(this IArray self) where T : IComparable - { - if (self.Count == 0) throw new ArgumentOutOfRangeException(); - return self.Aggregate(self[0], (a, b) => a.CompareTo(b) < 0 ? a : b); - } - - /// - /// Returns the maximum element in the list - /// - public static T Max(this IArray self) where T : IComparable - { - if (self.Count == 0) throw new ArgumentOutOfRangeException(); - return self.Aggregate(self[0], (a, b) => a.CompareTo(b) > 0 ? a : b); - } - - /// - /// Applies a function (like "+") to each element in the series to create an effect similar to partial sums. - /// - public static IArray Accumulate(this IArray self, Func f) - { - var n = self.Count; - var r = new T[n]; - if (n == 0) return r.ToIArray(); - var prev = r[0] = self[0]; - for (var i = 1; i < n; ++i) - { - prev = r[i] = f(prev, self[i]); - } - return r.ToIArray(); - } - - /// - /// 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 IArray PostAccumulate(this IArray 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.ToIArray(); - for (var i = 0; i < n; ++i) - { - prev = r[i + 1] = f(prev, self[i]); - } - return r.ToIArray(); - } - - /// - /// Returns true if the two lists are the same length, and the elements are the same. - /// - public static bool SequenceEquals(this IArray self, IArray other) where T : IEquatable - => self == other || (self.Count == other.Count && self.Zip(other, (x, y) => x?.Equals(y) ?? y == null).All(x => x)); - - /// - /// Creates a readonly array from a seed value, by applying a function - /// - public static IArray Build(T init, Func next, Func hasNext) - { - var r = new List(); - while (hasNext(init)) - { - r.Add(init); - init = next(init); - } - return r.ToIArray(); - } - - /// - /// Creates a readonly array from a seed value, by applying a function - /// - public static IArray Build(T init, Func next, Func hasNext) - { - var i = 0; - var r = new List(); - while (hasNext(init, i)) - { - r.Add(init); - init = next(init, ++i); - } - return r.ToIArray(); - } - - /// - /// Creates an array of arrays, split at the given indices - /// - public static IArray> Split(this IArray self, IArray indices) - => indices.Prepend(0).Zip(indices.Append(self.Count), (x, y) => self.Slice(x, y)); - - /// - /// Creates an array of arrays, split at the given index. - /// - public static IArray> Split(this IArray self, int index) - => Create(self.Take(index), self.Skip(index)); - - /// - /// Splits an array of tuples into a tuple of array - /// - public static (IArray, IArray) Unzip(this IArray<(T1, T2)> self) - => (self.Select(pair => pair.Item1), self.Select(pair => pair.Item2)); - - - /// - /// Returns true if the predicate is true for all of the elements in the array - /// - public static bool All(this IArray self, Func predicate) - => self.ToEnumerable().All(predicate); - - /// - /// Returns true if the predicate is true for any of the elements in the array - /// - public static bool Any(this IArray self, Func predicate) - => self.ToEnumerable().Any(predicate); - - /// - /// Sums items in an array using a selector function that returns integers. - /// - public static long Sum(this IArray self, Func func) - => self.Aggregate(0L, (init, x) => init + func(x)); - - /// - /// Sums items in an array using a selector function that returns doubles. - /// - public static double Sum(this IArray self, Func func) - => self.Aggregate(0.0, (init, x) => init + func(x)); - - /// - /// Forces evaluation (aka reification) of the array by creating a copy in memory. - /// This is useful as a performance optimization, or to force the objects to exist permanently. - /// - public static IArray Evaluate(this IArray x) - => (x is ArrayAdapter) ? x : x.ToArray().ToIArray(); - - /// - /// Forces evaluation (aka reification) of the array in parallel. - /// - public static IArray EvaluateInParallel(this IArray x) - => (x is ArrayAdapter) ? x : x.ToArrayInParallel().ToIArray(); - - /// - /// Converts to a regular array in paralle; - /// - /// - /// - /// - public static T[] ToArrayInParallel(this IArray xs) - { - if (xs.Count == 0) - return Array.Empty(); - - if (xs.Count < Environment.ProcessorCount) - return xs.ToArray(); - - var r = new T[xs.Count]; - var partitioner = Partitioner.Create(0, xs.Count, xs.Count / Environment.ProcessorCount); - - Parallel.ForEach(partitioner, (range, state) => - { - for (var i = range.Item1; i < range.Item2; ++i) - r[i] = xs[i]; - }); - return r; - } - - /// - /// Maps pairs of elements to a new array. - /// - public static IArray SelectPairs(this IArray xs, Func f) - => (xs.Count / 2).Select(i => f(xs[i * 2], xs[i * 2 + 1])); - - /// - /// Maps every 3 elements to a new array. - /// - public static IArray SelectTriplets(this IArray xs, Func f) - => (xs.Count / 3).Select(i => f(xs[i * 3], xs[i * 3 + 1], xs[i * 3 + 2])); - - /// - /// Maps every 4 elements to a new array. - /// - public static IArray SelectQuartets(this IArray xs, Func f) - => (xs.Count / 4).Select(i => f(xs[i * 4], xs[i * 4 + 1], xs[i * 4 + 2], xs[i * 4 + 3])); - - /// - /// Returns the number of unique instances of elements in the array. - /// - public static int CountUnique(this IArray xs) - => xs.ToEnumerable().Distinct().Count(); - - /// - /// Returns elements in order. - /// - public static IArray Sort(this IArray xs) where T : IComparable - => xs.ToEnumerable().OrderBy(x => x).ToIArray(); - - /// - /// Given an array of elements of type T casts them to a U - /// - public static IArray Cast(this IArray xs) where T : U - => xs.Select(x => (U)x); - - /// - /// Returns true if the value is present in the array. - /// - public static bool Contains(this IArray xs, T value) - => xs.Any(x => x.Equals(value)); - - public static ILookup ToLookup(this IEnumerable input, Func keyFunc, Func valueFunc) - => input.ToDictionary(keyFunc, valueFunc).ToLookup(); - - public static ILookup ToLookup(this IArray input, Func keyFunc, Func valueFunc) - => input.ToEnumerable().ToLookup(keyFunc, valueFunc); - - public static ILookup ToLookup(this IEnumerable input, Func keyFunc) - => input.ToDictionary(keyFunc, x => x).ToLookup(); - - public static ILookup ToLookup(this IArray input, Func keyFunc) - => input.ToEnumerable().ToLookup(keyFunc, x => x); - - public static T FirstOrDefault(this IArray xs) - => xs.Count > 0 ? xs[0] : default; - - public static T FirstOrDefault(this IArray xs, T @default) - => xs.Count > 0 ? xs[0] : @default; - - public static T FirstOrDefault(this IArray xs, Func predicate) - => xs.Where(predicate).FirstOrDefault(); - - public static IArray ToLongs(this IArray xs) - => xs.Select(x => (long)x); - - public static IArray PrefixSums(this IArray self) - => self.ToLongs().PrefixSums(); - - public static IArray PrefixSums(this IArray self) - => self.Scan(0f, (a, b) => a + b); - - public static IArray PrefixSums(this IArray self) - => self.Scan(0.0, (a, b) => a + b); - - public static IArray Scan(this IArray self, U init, Func scanFunc) - { - if (self.Count == 0) - return Empty(); - var r = new U[self.Count]; - for (var i = 0; i < self.Count; ++i) - init = r[i] = scanFunc(init, self[i]); - return r.ToIArray(); - } - - public static IArray PrefixSums(this IArray counts) - => counts.Scan(0L, (a, b) => a + b); - - // Similar to prefix sums, but starts at zero. - // r[i] = Sum(count[0 to i]) - public static IArray CountsToOffsets(this IArray counts) - { - var r = new int[counts.Count]; - for (var i = 1; i < counts.Count; ++i) - r[i] = r[i - 1] + counts[i - 1]; - return r.ToIArray(); - } - - public static IArray OffsetsToCounts(this IArray offsets, int last) - => offsets.Indices().Select(i => i < offsets.Count - 1 ? offsets[i + 1] - offsets[i] : last - offsets[i]); - - public static IArray SetElementAt(this IArray self, int index, T value) - => self.SelectIndices(i => i == index ? value : self[i]); - - public static IArray SetFirstElementWhere(this IArray self, Func predicate, T value) - { - var index = self.IndexOf(predicate); - if (index < 0) - return self; - return self.SetElementAt(index, value); - } - } -} diff --git a/src/cs/linqarray/Vim.LinqArray/Vim.LinqArray.csproj b/src/cs/linqarray/Vim.LinqArray/Vim.LinqArray.csproj index 110fabe9..558465f5 100644 --- a/src/cs/linqarray/Vim.LinqArray/Vim.LinqArray.csproj +++ b/src/cs/linqarray/Vim.LinqArray/Vim.LinqArray.csproj @@ -10,7 +10,7 @@ https://github.com/vimaec/linqarray GitHub true - license.txt + LICENSE.txt 1.1.3 true true @@ -27,7 +27,7 @@ - + True diff --git a/src/cs/math3d/Vim.Math3D.Tests/Vim.Math3D.Tests.csproj b/src/cs/math3d/Vim.Math3D.Tests/Vim.Math3D.Tests.csproj index a6f30d42..e4e11fda 100644 --- a/src/cs/math3d/Vim.Math3D.Tests/Vim.Math3D.Tests.csproj +++ b/src/cs/math3d/Vim.Math3D.Tests/Vim.Math3D.Tests.csproj @@ -12,7 +12,13 @@ - + + + + + True + + diff --git a/src/cs/math3d/Vim.Math3D/LinqUtil.cs b/src/cs/math3d/Vim.Math3D/AABoxUtil.cs similarity index 57% rename from src/cs/math3d/Vim.Math3D/LinqUtil.cs rename to src/cs/math3d/Vim.Math3D/AABoxUtil.cs index c105f6e4..e5d3b854 100644 --- a/src/cs/math3d/Vim.Math3D/LinqUtil.cs +++ b/src/cs/math3d/Vim.Math3D/AABoxUtil.cs @@ -1,10 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Collections.Generic; namespace Vim.Math3d { - public static class LinqUtil + public static class AABoxUtil { public static AABox ToAABox(this IEnumerable self) => AABox.Create(self); diff --git a/src/cs/math3d/Vim.Math3D/Vim.Math3D.csproj b/src/cs/math3d/Vim.Math3D/Vim.Math3D.csproj index b2138f57..ac941449 100644 --- a/src/cs/math3d/Vim.Math3D/Vim.Math3D.csproj +++ b/src/cs/math3d/Vim.Math3D/Vim.Math3D.csproj @@ -10,7 +10,7 @@ https://github.com/vimaec/math3d GitHub true - license.txt + LICENSE.txt 1.6.2 true true @@ -60,9 +60,9 @@ Structs.tt - + - + True diff --git a/src/cs/util/Vim.Util.Logging.Serilog/Vim.Util.Logging.Serilog.csproj b/src/cs/util/Vim.Util.Logging.Serilog/Vim.Util.Logging.Serilog.csproj index 31723bd0..ab531508 100644 --- a/src/cs/util/Vim.Util.Logging.Serilog/Vim.Util.Logging.Serilog.csproj +++ b/src/cs/util/Vim.Util.Logging.Serilog/Vim.Util.Logging.Serilog.csproj @@ -12,7 +12,13 @@ - + + + + True + + + diff --git a/src/cs/util/Vim.Util.Tests/Properties/Resources.Designer.cs b/src/cs/util/Vim.Util.Tests/Properties/Resources.Designer.cs index 2d371c8f..d31f3de6 100644 --- a/src/cs/util/Vim.Util.Tests/Properties/Resources.Designer.cs +++ b/src/cs/util/Vim.Util.Tests/Properties/Resources.Designer.cs @@ -61,7 +61,7 @@ internal Resources() { } /// - /// Looks up a localized string similar to C:\DEV\vimaec\src\Vim\vim-format\src\cs\util\Vim.Util.Tests\ + /// Looks up a localized string similar to C:\Users\Rober\Desktop\Vim\vim-format\src\cs\util\Vim.Util.Tests\ ///. /// internal static string ProjDir { diff --git a/src/cs/g3d/Vim.G3d.Tests/Util.cs b/src/cs/util/Vim.Util.Tests/TestUtils.cs similarity index 69% rename from src/cs/g3d/Vim.G3d.Tests/Util.cs rename to src/cs/util/Vim.Util.Tests/TestUtils.cs index 2ff62684..db4f642a 100644 --- a/src/cs/g3d/Vim.G3d.Tests/Util.cs +++ b/src/cs/util/Vim.Util.Tests/TestUtils.cs @@ -2,11 +2,40 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; +using System.Xml.Linq; -namespace Vim.G3d.Tests +namespace Vim.Util.Tests { - public static class Util + public static class TestUtils { + + public static string ResidencePath = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + + /// + /// Deletes and/or creates a folder for given test case name. + /// + public static string PrepareTestDir([CallerMemberName] string testName = null) + { + if (testName == null) + throw new ArgumentException(nameof(testName)); + + var testDir = Path.Combine(VimFormatRepoPaths.OutDir, testName); + + // Prepare the test directory + if (Directory.Exists(testDir)) + Directory.Delete(testDir, true); + Directory.CreateDirectory(testDir); + + return testDir; + } + + public static string PrepareOutputPath(string fileName) + { + var outputFolder = PrepareTestDir("Can_Convert_And_Read_Vimx"); + return Path.Combine(outputFolder, fileName); + } + public static (long, long) GetMemoryConsumptionAndMSecElapsed(Action action) { var time = 0L; @@ -72,5 +101,6 @@ private static long GetMemoryConsumption(Action action) GC.WaitForPendingFinalizers(); return GC.GetTotalMemory(true) - memBefore; } + } -} \ No newline at end of file +} diff --git a/src/cs/util/Vim.Util.Tests/Vim.Util.Tests.csproj b/src/cs/util/Vim.Util.Tests/Vim.Util.Tests.csproj index 8b38f884..6d419428 100644 --- a/src/cs/util/Vim.Util.Tests/Vim.Util.Tests.csproj +++ b/src/cs/util/Vim.Util.Tests/Vim.Util.Tests.csproj @@ -15,8 +15,9 @@ - - + + + @@ -37,5 +38,11 @@ + + + True + + + diff --git a/src/cs/util/Vim.Util.Tests/VimFormatRepoPaths.cs b/src/cs/util/Vim.Util.Tests/VimFormatRepoPaths.cs index 2a76d8b7..5816c16f 100644 --- a/src/cs/util/Vim.Util.Tests/VimFormatRepoPaths.cs +++ b/src/cs/util/Vim.Util.Tests/VimFormatRepoPaths.cs @@ -26,7 +26,7 @@ public static class VimFormatRepoPaths /// public static string GetLatestWolfordResidenceVim() { - var matchingVim = Directory.GetFiles(DataDir, "Wolford_Residence*.vim", SearchOption.AllDirectories).FirstOrDefault(); + var matchingVim = Directory.GetFiles(DataDir, "Wolford_Residence*.vim", SearchOption.AllDirectories).OrderByDescending(p => p).FirstOrDefault(); if (matchingVim == null) throw new FileNotFoundException($"Could not find the latest Wolford Residence VIM."); diff --git a/src/cs/util/Vim.Util/LinqExtensions.cs b/src/cs/util/Vim.Util/CollectionExtensions.cs similarity index 76% rename from src/cs/util/Vim.Util/LinqExtensions.cs rename to src/cs/util/Vim.Util/CollectionExtensions.cs index ae41cd0e..7039b7de 100644 --- a/src/cs/util/Vim.Util/LinqExtensions.cs +++ b/src/cs/util/Vim.Util/CollectionExtensions.cs @@ -2,12 +2,70 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text; namespace Vim.Util { - public static class LinqExtensions + public static class CollectionExtensions { + /// + /// Returns the index of the first element matching the given item. + /// + public static int IndexOf(this T[] self, T item) where T : IEquatable + => self.IndexOf(x => x.Equals(item)); + + /// + /// Returns the index of the first element matching the given item. + /// + public static int IndexOf(this T[] self, Func predicate) + { + for (var i = 0; i < self.Length; ++i) + { + if (predicate(self[i])) + return i; + } + + return -1; + } + + + + /// + /// Returns an IEnumerable containing only indices of the array for which the function satisfies a specific predicate. + /// + public static IEnumerable IndicesWhere(this IList self, Func predicate) + { + for (var i = 0; i < self.Count; ++i) + { + if (predicate(self[i])) + yield return i; + } + } + + /// + /// Returns an IEnumerable containing only indices of the array for which the function satisfies a specific predicate. + /// + public static IEnumerable IndicesWhere(this IList self, Func predicate) + { + for (var i = 0; i < self.Count; ++i) + { + if (predicate(self[i], i)) + yield return i; + } + } + + /// + /// [a, b, c ..] => [0, a, a+b, a+b+c ...] + /// + public static int[] OffsetsFromCounts(this IList self) + { + var result = new int[self.Count]; + for (var i = 1; i < self.Count; ++i) + { + result[i] = result[i - 1] + self[i - 1]; + } + return result; + } + /// /// Returns the top of a stack, or the default T value if none is present. /// @@ -27,13 +85,7 @@ public static T OneOrDefault(this IEnumerable values) return items?.Count == 1 ? items[0] : default; } - /// - /// A helper function for append one or more items to an IEnumerable. - /// - public static IEnumerable Append(this IEnumerable xs, params T[] x) - => xs.Concat(x); - - public static T ElementAtOrDefault(this IReadOnlyList items, int index, T @default) + public static T ElementAtOrDefault(this IReadOnlyList items, int index, T @default = default(T)) => index < 0 || index >= items.Count ? @default : items[index]; /// diff --git a/src/cs/util/Vim.Util/Vim.Util.csproj b/src/cs/util/Vim.Util/Vim.Util.csproj index dbdcea46..fc964269 100644 --- a/src/cs/util/Vim.Util/Vim.Util.csproj +++ b/src/cs/util/Vim.Util/Vim.Util.csproj @@ -4,4 +4,10 @@ netstandard2.0 + + + True + + + diff --git a/src/cs/vim-format.sln b/src/cs/vim-format.sln index ba3ae0fe..dca052d8 100644 --- a/src/cs/vim-format.sln +++ b/src/cs/vim-format.sln @@ -5,16 +5,6 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "bfast", "bfast", "{F3260C54-834F-4C74-A3B7-EAB622AFA492}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.BFast", "bfast\Vim.BFast\Vim.BFast.csproj", "{1059A7DE-95FA-4F54-85E3-84049E1872D5}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.BFast.Tests", "bfast\Vim.BFast.Tests\Vim.BFast.Tests.csproj", "{000ED0CF-EC9C-488C-BE85-14B95FFDF104}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "linqarray", "linqarray", "{CF54EDFD-51AB-4B0D-B084-2DF42918BA51}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.LinqArray", "linqarray\Vim.LinqArray\Vim.LinqArray.csproj", "{07CA1F46-21DA-4C22-A8CB-52D526D51C94}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.LinqArray.Tests", "linqarray\Vim.LinqArray.Tests\Vim.LinqArray.Tests.csproj", "{461788B0-072C-485F-82C0-F83CCC86F95D}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "math3d", "math3d", "{9BD3CC85-97FD-4093-AA11-58FE405B0F65}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.Math3D", "math3d\Vim.Math3D\Vim.Math3D.csproj", "{1086F24E-32C8-4261-9B06-A364EEE71DEF}" @@ -27,12 +17,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.Format", "vim\Vim.Forma EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "g3d", "g3d", "{705B8FB2-D707-4527-91FC-0AADEDBA4D80}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.G3d.Tests", "g3d\Vim.G3d.Tests\Vim.G3d.Tests.csproj", "{DFC8DC5E-377D-4CCC-A498-5894D891A471}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.G3d", "g3d\Vim.G3d\Vim.G3d.csproj", "{F1EC1E94-C74C-4C37-961C-305CAE7C4707}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.G3d.AssimpWrapper", "g3d\Vim.G3d.AssimpWrapper\Vim.G3d.AssimpWrapper.csproj", "{16DBF49F-3E0C-4CD3-BB6C-1BBAF0D89AC6}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.Format.CodeGen", "vim\Vim.Format.CodeGen\Vim.Format.CodeGen.csproj", "{762E8B1A-9182-4C5D-A25C-0C0F6219BA4E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.Format.Tests", "vim\Vim.Format.Tests\Vim.Format.Tests.csproj", "{2CB4CF73-0CA8-4595-910C-EDA244EA5983}" @@ -45,7 +29,23 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.Util.Tests", "util\Vim. EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "util", "util", "{A279C2F9-3418-484B-A36E-D1D1C67A0B2D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vim.Util.Logging.Serilog", "util\Vim.Util.Logging.Serilog\Vim.Util.Logging.Serilog.csproj", "{6B9E6432-A7BB-4487-905A-0C3117398140}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.Util.Logging.Serilog", "util\Vim.Util.Logging.Serilog\Vim.Util.Logging.Serilog.csproj", "{6B9E6432-A7BB-4487-905A-0C3117398140}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.Format.Vimx", "vim\Vim.Format.Vimx\Vim.Format.Vimx.csproj", "{B5C8E733-8D3F-45BD-BBBE-09A9F1965545}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.Format.Vimx.Test", "vim\Vim.Vimx.Test\Vim.Format.Vimx.Test.csproj", "{BBF94CBD-CD39-49F5-979A-5C9E52C29330}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.BFast", "bfast\Vim.BFast\Vim.BFast.csproj", "{408884EA-3CE5-4A34-97F6-1F2D64A0E745}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.BFast.Tests", "bfast\Vim.BFast.Tests\Vim.BFast.Tests.csproj", "{FD149D64-5905-4F7D-97A8-9F7DA18A257D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.Format.Vimx.Conversion", "vim\Vim.Format.Vimx.Conversion\Vim.Format.Vimx.Conversion.csproj", "{4C4F9826-0DEF-4A39-BFC8-A834522694A0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.G3d", "g3d\Vim.G3d\Vim.G3d.csproj", "{4D3789CE-5C2F-4B4E-8E9F-EC0D171E520E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.G3d.CodeGen", "g3d\Vim.G3d.CodeGen\Vim.G3d.CodeGen.csproj", "{DC267698-FA79-40E2-9322-14F346ABAD85}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vim.G3d.Tests", "g3d\Vim.G3d.Tests\Vim.G3d.Tests.csproj", "{059CAAF3-0B7C-46B0-A939-BF96DC6DB394}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -53,22 +53,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1059A7DE-95FA-4F54-85E3-84049E1872D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1059A7DE-95FA-4F54-85E3-84049E1872D5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1059A7DE-95FA-4F54-85E3-84049E1872D5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1059A7DE-95FA-4F54-85E3-84049E1872D5}.Release|Any CPU.Build.0 = Release|Any CPU - {000ED0CF-EC9C-488C-BE85-14B95FFDF104}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {000ED0CF-EC9C-488C-BE85-14B95FFDF104}.Debug|Any CPU.Build.0 = Debug|Any CPU - {000ED0CF-EC9C-488C-BE85-14B95FFDF104}.Release|Any CPU.ActiveCfg = Release|Any CPU - {000ED0CF-EC9C-488C-BE85-14B95FFDF104}.Release|Any CPU.Build.0 = Release|Any CPU - {07CA1F46-21DA-4C22-A8CB-52D526D51C94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {07CA1F46-21DA-4C22-A8CB-52D526D51C94}.Debug|Any CPU.Build.0 = Debug|Any CPU - {07CA1F46-21DA-4C22-A8CB-52D526D51C94}.Release|Any CPU.ActiveCfg = Release|Any CPU - {07CA1F46-21DA-4C22-A8CB-52D526D51C94}.Release|Any CPU.Build.0 = Release|Any CPU - {461788B0-072C-485F-82C0-F83CCC86F95D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {461788B0-072C-485F-82C0-F83CCC86F95D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {461788B0-072C-485F-82C0-F83CCC86F95D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {461788B0-072C-485F-82C0-F83CCC86F95D}.Release|Any CPU.Build.0 = Release|Any CPU {1086F24E-32C8-4261-9B06-A364EEE71DEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1086F24E-32C8-4261-9B06-A364EEE71DEF}.Debug|Any CPU.Build.0 = Debug|Any CPU {1086F24E-32C8-4261-9B06-A364EEE71DEF}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -81,18 +65,6 @@ Global {22278D91-27D7-4A84-8170-5FFB77A70C1F}.Debug|Any CPU.Build.0 = Debug|Any CPU {22278D91-27D7-4A84-8170-5FFB77A70C1F}.Release|Any CPU.ActiveCfg = Release|Any CPU {22278D91-27D7-4A84-8170-5FFB77A70C1F}.Release|Any CPU.Build.0 = Release|Any CPU - {DFC8DC5E-377D-4CCC-A498-5894D891A471}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DFC8DC5E-377D-4CCC-A498-5894D891A471}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DFC8DC5E-377D-4CCC-A498-5894D891A471}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DFC8DC5E-377D-4CCC-A498-5894D891A471}.Release|Any CPU.Build.0 = Release|Any CPU - {F1EC1E94-C74C-4C37-961C-305CAE7C4707}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1EC1E94-C74C-4C37-961C-305CAE7C4707}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1EC1E94-C74C-4C37-961C-305CAE7C4707}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1EC1E94-C74C-4C37-961C-305CAE7C4707}.Release|Any CPU.Build.0 = Release|Any CPU - {16DBF49F-3E0C-4CD3-BB6C-1BBAF0D89AC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {16DBF49F-3E0C-4CD3-BB6C-1BBAF0D89AC6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {16DBF49F-3E0C-4CD3-BB6C-1BBAF0D89AC6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {16DBF49F-3E0C-4CD3-BB6C-1BBAF0D89AC6}.Release|Any CPU.Build.0 = Release|Any CPU {762E8B1A-9182-4C5D-A25C-0C0F6219BA4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {762E8B1A-9182-4C5D-A25C-0C0F6219BA4E}.Debug|Any CPU.Build.0 = Debug|Any CPU {762E8B1A-9182-4C5D-A25C-0C0F6219BA4E}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -117,27 +89,60 @@ Global {6B9E6432-A7BB-4487-905A-0C3117398140}.Debug|Any CPU.Build.0 = Debug|Any CPU {6B9E6432-A7BB-4487-905A-0C3117398140}.Release|Any CPU.ActiveCfg = Release|Any CPU {6B9E6432-A7BB-4487-905A-0C3117398140}.Release|Any CPU.Build.0 = Release|Any CPU + {B5C8E733-8D3F-45BD-BBBE-09A9F1965545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5C8E733-8D3F-45BD-BBBE-09A9F1965545}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5C8E733-8D3F-45BD-BBBE-09A9F1965545}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5C8E733-8D3F-45BD-BBBE-09A9F1965545}.Release|Any CPU.Build.0 = Release|Any CPU + {BBF94CBD-CD39-49F5-979A-5C9E52C29330}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BBF94CBD-CD39-49F5-979A-5C9E52C29330}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BBF94CBD-CD39-49F5-979A-5C9E52C29330}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BBF94CBD-CD39-49F5-979A-5C9E52C29330}.Release|Any CPU.Build.0 = Release|Any CPU + {408884EA-3CE5-4A34-97F6-1F2D64A0E745}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {408884EA-3CE5-4A34-97F6-1F2D64A0E745}.Debug|Any CPU.Build.0 = Debug|Any CPU + {408884EA-3CE5-4A34-97F6-1F2D64A0E745}.Release|Any CPU.ActiveCfg = Release|Any CPU + {408884EA-3CE5-4A34-97F6-1F2D64A0E745}.Release|Any CPU.Build.0 = Release|Any CPU + {FD149D64-5905-4F7D-97A8-9F7DA18A257D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD149D64-5905-4F7D-97A8-9F7DA18A257D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD149D64-5905-4F7D-97A8-9F7DA18A257D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD149D64-5905-4F7D-97A8-9F7DA18A257D}.Release|Any CPU.Build.0 = Release|Any CPU + {4C4F9826-0DEF-4A39-BFC8-A834522694A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4C4F9826-0DEF-4A39-BFC8-A834522694A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4C4F9826-0DEF-4A39-BFC8-A834522694A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4C4F9826-0DEF-4A39-BFC8-A834522694A0}.Release|Any CPU.Build.0 = Release|Any CPU + {4D3789CE-5C2F-4B4E-8E9F-EC0D171E520E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D3789CE-5C2F-4B4E-8E9F-EC0D171E520E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D3789CE-5C2F-4B4E-8E9F-EC0D171E520E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D3789CE-5C2F-4B4E-8E9F-EC0D171E520E}.Release|Any CPU.Build.0 = Release|Any CPU + {DC267698-FA79-40E2-9322-14F346ABAD85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DC267698-FA79-40E2-9322-14F346ABAD85}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DC267698-FA79-40E2-9322-14F346ABAD85}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DC267698-FA79-40E2-9322-14F346ABAD85}.Release|Any CPU.Build.0 = Release|Any CPU + {059CAAF3-0B7C-46B0-A939-BF96DC6DB394}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {059CAAF3-0B7C-46B0-A939-BF96DC6DB394}.Debug|Any CPU.Build.0 = Debug|Any CPU + {059CAAF3-0B7C-46B0-A939-BF96DC6DB394}.Release|Any CPU.ActiveCfg = Release|Any CPU + {059CAAF3-0B7C-46B0-A939-BF96DC6DB394}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {1059A7DE-95FA-4F54-85E3-84049E1872D5} = {F3260C54-834F-4C74-A3B7-EAB622AFA492} - {000ED0CF-EC9C-488C-BE85-14B95FFDF104} = {F3260C54-834F-4C74-A3B7-EAB622AFA492} - {07CA1F46-21DA-4C22-A8CB-52D526D51C94} = {CF54EDFD-51AB-4B0D-B084-2DF42918BA51} - {461788B0-072C-485F-82C0-F83CCC86F95D} = {CF54EDFD-51AB-4B0D-B084-2DF42918BA51} {1086F24E-32C8-4261-9B06-A364EEE71DEF} = {9BD3CC85-97FD-4093-AA11-58FE405B0F65} {1CC20AA4-11F1-4548-9285-FD24D42190FB} = {9BD3CC85-97FD-4093-AA11-58FE405B0F65} {22278D91-27D7-4A84-8170-5FFB77A70C1F} = {D639C635-01D1-48C4-89B5-1FD70F1AFEE9} - {DFC8DC5E-377D-4CCC-A498-5894D891A471} = {705B8FB2-D707-4527-91FC-0AADEDBA4D80} - {F1EC1E94-C74C-4C37-961C-305CAE7C4707} = {705B8FB2-D707-4527-91FC-0AADEDBA4D80} - {16DBF49F-3E0C-4CD3-BB6C-1BBAF0D89AC6} = {705B8FB2-D707-4527-91FC-0AADEDBA4D80} {762E8B1A-9182-4C5D-A25C-0C0F6219BA4E} = {D639C635-01D1-48C4-89B5-1FD70F1AFEE9} {2CB4CF73-0CA8-4595-910C-EDA244EA5983} = {D639C635-01D1-48C4-89B5-1FD70F1AFEE9} {2C798994-518D-4EA5-8F8A-3EAA30269F9E} = {D639C635-01D1-48C4-89B5-1FD70F1AFEE9} {EC00B2EC-3CF0-43C3-A071-320AD3C355CF} = {A279C2F9-3418-484B-A36E-D1D1C67A0B2D} {F7091670-1059-4F4F-AC3A-0B1DE4A724B5} = {A279C2F9-3418-484B-A36E-D1D1C67A0B2D} {6B9E6432-A7BB-4487-905A-0C3117398140} = {A279C2F9-3418-484B-A36E-D1D1C67A0B2D} + {B5C8E733-8D3F-45BD-BBBE-09A9F1965545} = {D639C635-01D1-48C4-89B5-1FD70F1AFEE9} + {BBF94CBD-CD39-49F5-979A-5C9E52C29330} = {D639C635-01D1-48C4-89B5-1FD70F1AFEE9} + {408884EA-3CE5-4A34-97F6-1F2D64A0E745} = {F3260C54-834F-4C74-A3B7-EAB622AFA492} + {FD149D64-5905-4F7D-97A8-9F7DA18A257D} = {F3260C54-834F-4C74-A3B7-EAB622AFA492} + {4C4F9826-0DEF-4A39-BFC8-A834522694A0} = {D639C635-01D1-48C4-89B5-1FD70F1AFEE9} + {4D3789CE-5C2F-4B4E-8E9F-EC0D171E520E} = {705B8FB2-D707-4527-91FC-0AADEDBA4D80} + {DC267698-FA79-40E2-9322-14F346ABAD85} = {705B8FB2-D707-4527-91FC-0AADEDBA4D80} + {059CAAF3-0B7C-46B0-A939-BF96DC6DB394} = {705B8FB2-D707-4527-91FC-0AADEDBA4D80} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7D1BF641-26E8-4809-B638-029241C51BE2} diff --git a/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs b/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs index d386e613..6b6fcbba 100644 --- a/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs +++ b/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs @@ -71,7 +71,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(); @@ -80,9 +80,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"; @@ -94,9 +94,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;"); } @@ -105,7 +105,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)"); @@ -231,7 +231,7 @@ private static CodeBuilder WriteDocument(CodeBuilder cb = null) 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(); @@ -265,7 +265,7 @@ private static CodeBuilder WriteDocument(CodeBuilder cb = null) 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"); @@ -350,8 +350,8 @@ 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..dd3329e7 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 { @@ -123,7 +123,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); 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 7e1d8ac0..333323fd 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs @@ -2,16 +2,12 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using Vim.BFast; -using Vim.LinqArray; +using Vim.BFastLib; namespace Vim.Format { public static partial class ColumnExtensions { - public static IEnumerable GetAllColumns(this SerializableEntityTable et) - => et.DataColumns.Concat(et.IndexColumns).Concat(et.StringColumns).ToList(); - public static void ValidateColumnRowsAreAligned(this IEnumerable columns) { var numRows = columns.FirstOrDefault()?.NumElements() ?? 0; @@ -29,17 +25,17 @@ public static void ValidateColumnRowsAreAligned(this IEnumerable c public static SerializableEntityTable ValidateColumnRowsAreAligned(this SerializableEntityTable et) { - et.GetAllColumns().ValidateColumnRowsAreAligned(); + et.AllColumns.ValidateColumnRowsAreAligned(); return et; } public static string ValidateCanConcatBuffers(this INamedBuffer thisBuffer, INamedBuffer otherBuffer) { - var thisPrefix = thisBuffer.GetTypePrefix(); + var thisPrefix = SerializableEntityTable.GetTypeFromName(thisBuffer.Name); if (string.IsNullOrEmpty(thisPrefix)) throw new Exception("NamedBuffer prefix not found"); - var otherPrefix = otherBuffer.GetTypePrefix(); + var otherPrefix = SerializableEntityTable.GetTypeFromName(otherBuffer.Name); if (string.IsNullOrEmpty(otherPrefix)) throw new Exception("NamedBuffer prefix not found"); @@ -77,7 +73,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) { @@ -119,7 +118,7 @@ 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); } @@ -181,20 +180,8 @@ public static List> ConcatIntColumns(this IReadOnlyList 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) - => new SerializableEntityTable - { - Name = thisTable.Name, - IndexColumns = thisTable.IndexColumns.ConcatIntColumns(otherTable.IndexColumns), - StringColumns = thisTable.StringColumns.ConcatIntColumns(otherTable.StringColumns), - DataColumns = thisTable.DataColumns.ConcatDataColumns(otherTable.DataColumns), - }.ValidateColumnRowsAreAligned(); - - public static IArray GetColumnValues(this INamedBuffer nb) where T : unmanaged - => nb.AsArray().ToIArray(); + 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. @@ -203,7 +190,7 @@ public static IEnumerable> NoneIndexColumnRelations( this IEnumerable> indexColumns, params string[] indexColumnNames) => indexColumns.Select(ic => indexColumnNames.Contains(ic.Name) - ? new NamedBuffer(VimConstants.NoEntityRelation.Repeat(ic.Data.Length).ToArray(), ic.Name) + ? new NamedBuffer(Enumerable.Repeat(VimConstants.NoEntityRelation, ic.Data.Length).ToArray(), ic.Name) : ic); /// diff --git a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs index d7cedece..81f3efe3 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs @@ -60,9 +60,6 @@ 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) => t.Name == "Relation`1"; diff --git a/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs b/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs index 0d826c14..531e3619 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs @@ -19,9 +19,6 @@ 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 = AllColumnInfos .Where(t => t.ColumnType == ColumnType.DataColumn) diff --git a/src/cs/vim/Vim.Format.Core/Document.cs b/src/cs/vim/Vim.Format.Core/Document.cs index 476bd8e7..b83ae7ee 100644 --- a/src/cs/vim/Vim.Format.Core/Document.cs +++ b/src/cs/vim/Vim.Format.Core/Document.cs @@ -1,5 +1,8 @@ -using Vim.LinqArray; -using Vim.BFast; +using System.Collections.Generic; +using System.Linq; +using Vim.BFastLib; +using Vim.G3d; +using Vim.Util; namespace Vim.Format { @@ -10,21 +13,21 @@ 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); + Assets = _Document.Assets.ToDictionary(et => et.Name, et => et); } public string FileName => _Document.FileName; - private SerializableDocument _Document { get; } + public 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; } } } diff --git a/src/cs/vim/Vim.Format.Core/DocumentBuilder.cs b/src/cs/vim/Vim.Format.Core/DocumentBuilder.cs index 04c2372b..c5b1e68b 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,10 +15,7 @@ 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; } @@ -51,51 +49,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 { @@ -134,17 +145,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,12 +191,15 @@ public List ComputeEntityTables(IReadOnlyDictionary kv.Value.ToNamedBuffer(kv.Key)) as IEnumerable; Debug.Assert(assets != null, "Asset conversion to IEnumerable failed."); @@ -194,7 +208,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..da62d335 100644 --- a/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs @@ -1,54 +1,30 @@ using System.Collections.Generic; using System.Linq; -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.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) { var name = table.Name; var tb = db.CreateTableBuilder(name); - foreach (var col in table.IndexColumns.Values.ToEnumerable()) + foreach (var col in table.IndexColumns.Values) { tb.AddIndexColumn(col.Name, col.GetTypedData().RemapData(nodeIndexRemapping)); } - foreach (var col in table.DataColumns.Values.ToEnumerable()) + foreach (var col in table.DataColumns.Values) { tb.AddDataColumn(col.Name, col.CopyDataColumn(nodeIndexRemapping)); } - foreach (var col in table.StringColumns.Values.ToEnumerable()) + foreach (var col in table.StringColumns.Values) { var strings = col.GetTypedData().Select(i => table.Document.StringTable.ElementAtOrDefault(i, null)); tb.AddStringColumn(col.Name, strings.ToArray().RemapData(nodeIndexRemapping)); @@ -59,7 +35,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.EntityTables.Values) { var name = table.Name; diff --git a/src/cs/vim/Vim.Format.Core/DocumentBuilderTypes.cs b/src/cs/vim/Vim.Format.Core/DocumentBuilderTypes.cs index 497c4ffa..3d4b276f 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; } } /// @@ -71,6 +72,7 @@ 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(); @@ -100,8 +102,39 @@ public void AppendVertices(IEnumerable vertices) public void AppendUVs(IEnumerable uvs) => _uvs.AddRange(uvs); - public SubdividedMesh Subdivide() - => new SubdividedMesh(this); + public VimMesh Subdivide() + { + if (Indices.Any(i => i < 0 && i >= Vertices.Count)) + throw new Exception($"Invalid mesh. Indices out of vertex range."); + + var facesByMats = FaceMaterials + .Select((face, index) => (face, index)) + .GroupBy(pair => pair.face, pair => pair.index); + + var submeshIndexOffsets = new List(); + var submeshMaterials = new List(); + var indicesRemap = new List(); + + foreach (var group in facesByMats) + { + submeshIndexOffsets.Add(indicesRemap.Count); + submeshMaterials.Add(group.Key); + foreach (var face in group) + { + var f = face * 3; + indicesRemap.Add(Indices[f]); + indicesRemap.Add(Indices[f + 1]); + indicesRemap.Add(Indices[f + 2]); + } + } + return new VimMesh( + indicesRemap.ToArray(), + Vertices.ToArray(), + submeshIndexOffsets.ToArray(), + submeshMaterials.ToArray() + ); + + } } /// diff --git a/src/cs/vim/Vim.Format.Core/DocumentExtensions.cs b/src/cs/vim/Vim.Format.Core/DocumentExtensions.cs index 52e2c5e2..dfa6a02d 100644 --- a/src/cs/vim/Vim.Format.Core/DocumentExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/DocumentExtensions.cs @@ -1,7 +1,9 @@ using System; using System.Text.RegularExpressions; -using Vim.LinqArray; -using Vim.BFast; +using Vim.BFastLib; +using System.Collections.Generic; +using System.Linq; +using Vim.Util; namespace Vim.Format { @@ -13,30 +15,6 @@ public static Document ToDocument(this SerializableDocument document) public static EntityTable ToEntityTable(this SerializableEntityTable entityTable, Document document) => new EntityTable(document, entityTable); - public static IArray GetColumnNames(this EntityTable table) - => table.Columns.Select(b => b.Name); - - 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 @@ -63,12 +41,6 @@ public static IndexColumnNameComponents SplitIndexColumnName(string 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); @@ -77,11 +49,6 @@ public static EntityTable GetRelatedTable(this INamedBuffer ic, Document do 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/EntityTable.cs b/src/cs/vim/Vim.Format.Core/EntityTable.cs index a9e0b050..edc40e26 100644 --- a/src/cs/vim/Vim.Format.Core/EntityTable.cs +++ b/src/cs/vim/Vim.Format.Core/EntityTable.cs @@ -1,7 +1,8 @@ using System; -using System.Diagnostics; -using Vim.BFast; -using Vim.LinqArray; +using Vim.BFastLib; +using System.Linq; +using System.Collections.Generic; +using Vim.Util; namespace Vim.Format { @@ -13,35 +14,35 @@ 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); + 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.ToEnumerable().ValidateColumnRowsAreAligned(); + 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 IArray Columns + public Dictionary DataColumns { get; } + public Dictionary> StringColumns { get; } + public Dictionary> IndexColumns { get; } + public IEnumerable Columns => DataColumns.Values - .Concatenate(IndexColumns.Values.Select(x => (INamedBuffer)x)) - .Concatenate(StringColumns.Values.Select(x => (INamedBuffer)x)); + .Concat(IndexColumns.Values.Select(x => (INamedBuffer)x)) + .Concat(StringColumns.Values.Select(x => (INamedBuffer)x)); - public IArray GetIndexColumnValues(string columnName) + public int[] GetIndexColumnValues(string columnName) => IndexColumns.GetOrDefault(columnName)?.GetColumnValues(); - public IArray GetStringColumnValues(string columnName) + public string[] GetStringColumnValues(string columnName) => StringColumns.GetOrDefault(columnName) ?.GetColumnValues() - ?.Select(Document.GetString); + ?.Select(Document.GetString).ToArray(); - public IArray GetDataColumnValues(string columnName) where T : unmanaged + public T[] GetDataColumnValues(string columnName) where T : unmanaged { var type = typeof(T); @@ -53,11 +54,10 @@ public IArray GetDataColumnValues(string columnName) where T : unmanaged return null; if (type == typeof(short)) - return namedBuffer.GetColumnValues().Select(i => (short)i) as IArray; + return namedBuffer.GetColumnValues().Select(i => (short)i) as T[]; if (type == typeof(bool)) - return namedBuffer.GetColumnValues().Select(b => b != 0) as IArray; - + return namedBuffer.GetColumnValues().Select(b => b != 0) as T[]; return namedBuffer.GetColumnValues(); } } diff --git a/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs b/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs index bd1da0fd..08582b99 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 { 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..8cbdbd45 --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/G3dBuilder.cs @@ -0,0 +1,118 @@ +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 AddInstance(Instance instance) + { + _instances.Add(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 => m.indices)); + 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 index 4ffbd54a..60bd68bd 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/CatmullClark.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/CatmullClark.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Vim.LinqArray; using Vim.Math3d; namespace Vim.Format.Geometry @@ -39,7 +38,7 @@ public class GeometryEdge public static class CatmullClarkSmoothing { - public static Dictionary CreateEdgeMap(this IMesh geometry) + public static Dictionary CreateEdgeMap(this VimMesh geometry) { var globalEdgeIndex = 0; var edgeMap = new Dictionary(); @@ -49,7 +48,7 @@ public static Dictionary CreateEdgeMap(this IMesh var faceSize = 3; for (var edgeIndex = 0; edgeIndex < faceSize; edgeIndex++) { - var geometryEdgeKey = new GeometryEdgeKey(geometry.Indices[currentIndex + edgeIndex], geometry.Indices[currentIndex + (edgeIndex + 1) % faceSize]); + var geometryEdgeKey = new GeometryEdgeKey(geometry.indices[currentIndex + edgeIndex], geometry.indices[currentIndex + (edgeIndex + 1) % faceSize]); if (edgeMap.ContainsKey(geometryEdgeKey)) { edgeMap[geometryEdgeKey].Face1 = faceIndex; @@ -68,21 +67,21 @@ public static Dictionary CreateEdgeMap(this IMesh return edgeMap; } - public static IMesh CatmullClark(this IMesh geometry, float smoothing = 0.0f) + public static VimMesh CatmullClark(this VimMesh geometry, float smoothing = 0.0f) { var edgeMap = geometry.CreateEdgeMap(); var numQuads = geometry.NumFaces * 3; - var numVertices = geometry.Vertices.Count + edgeMap.Count + geometry.NumFaces; + var numVertices = geometry.vertices.Length + 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 vertexFPoints = new Vector3[geometry.vertices.Length]; + var vertexRPoints = new Vector3[geometry.vertices.Length]; + var vertexNumFaces = new float[geometry.vertices.Length]; + var vertexNumEdges = new float[geometry.vertices.Length]; var newVertices = new Vector3[numVertices]; var newIndices = new int[numQuads * 4]; - var edgeVertices = new bool[geometry.Vertices.Count]; + var edgeVertices = new bool[geometry.vertices.Length]; foreach (var edge in edgeMap) { @@ -113,7 +112,7 @@ public static IMesh CatmullClark(this IMesh geometry, float smoothing = 0.0f) CalculateEdgePoints(geometry, edgeMap, facePoints, vertexNumEdges, vertexRPoints, smoothing); CalculateVertexPoints(geometry, vertexNumFaces, vertexFPoints, vertexNumEdges, vertexRPoints, newVertices, edgeVertices, smoothing); - var facePointStartIndex = geometry.Vertices.Count; + var facePointStartIndex = geometry.vertices.Length; var edgePointStartIndex = facePointStartIndex + facePoints.Length; // Copy all points into a single buffer @@ -129,7 +128,7 @@ public static IMesh CatmullClark(this IMesh geometry, float smoothing = 0.0f) return GenerateMesh(geometry, newVertices, newIndices, edgeMap, facePointStartIndex, edgePointStartIndex, numQuads); } - public static void CalculateFaceCentroidPoints(IMesh geometry, Vector3[] outFacePoints, float[] outVertexNumFaces, Vector3[] outVertexFPoints) + public static void CalculateFaceCentroidPoints(VimMesh geometry, Vector3[] outFacePoints, float[] outVertexNumFaces, Vector3[] outVertexFPoints) { var currentVertexIndex = 0; for (var faceIndex = 0; faceIndex < geometry.NumFaces; faceIndex++) @@ -138,10 +137,10 @@ public static void CalculateFaceCentroidPoints(IMesh geometry, Vector3[] outFace var facePoint = new Vector3(); - for (var edgeIndex = 0; edgeIndex < faceSize; edgeIndex++) + for (var edgeIndex = 0; edgeIndex < 3; edgeIndex++) { - var vertexIndex = geometry.Indices[currentVertexIndex + edgeIndex]; - facePoint += geometry.Vertices[vertexIndex]; + var vertexIndex = geometry.indices[currentVertexIndex + edgeIndex]; + facePoint += geometry.vertices[vertexIndex]; } facePoint /= faceSize; @@ -150,7 +149,7 @@ public static void CalculateFaceCentroidPoints(IMesh geometry, Vector3[] outFace for (var edgeIndex = 0; edgeIndex < faceSize; edgeIndex++) { - var vertexIndex = geometry.Indices[currentVertexIndex + edgeIndex]; + var vertexIndex = geometry.indices[currentVertexIndex + edgeIndex]; outVertexNumFaces[vertexIndex]++; outVertexFPoints[vertexIndex] += facePoint; } @@ -159,12 +158,12 @@ public static void CalculateFaceCentroidPoints(IMesh geometry, Vector3[] outFace } } - public static void CalculateEdgePoints(IMesh geometry, Dictionary edgeMap, Vector3[] facePoints, float[] outVertexNumEdges, Vector3[] outVertexRPoints, float smoothing) + public static void CalculateEdgePoints(VimMesh 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 middlePoint = geometry.vertices[edge.Key.Vertex0] + geometry.vertices[edge.Key.Vertex1]; var edgePoint = middlePoint; if (edge.Value.Face0 >= 0 && edge.Value.Face1 >= 0) @@ -186,19 +185,19 @@ public static void CalculateEdgePoints(IMesh geometry, Dictionary edgeMap, int facePointStartIndex, int edgePointStartIndex, int numQuads) + public static VimMesh GenerateMesh(VimMesh geometry, Vector3[] newVertices, int[] newIndices, Dictionary edgeMap, int facePointStartIndex, int edgePointStartIndex, int numQuads) { var currentNewVertexIndex = 0; var currentOldVertexIndex = 0; @@ -208,9 +207,9 @@ public static IMesh GenerateMesh(IMesh geometry, Vector3[] newVertices, int[] ne 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 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); @@ -224,7 +223,7 @@ public static IMesh GenerateMesh(IMesh geometry, Vector3[] newVertices, int[] ne currentOldVertexIndex += faceSize; } - return Primitives.QuadMesh(newVertices.ToIArray(), newIndices.ToIArray()); + return new VimMesh(newIndices, newVertices); } } } diff --git a/src/cs/vim/Vim.Format.Core/Geometry/GeometryCuttingUtils.cs b/src/cs/vim/Vim.Format.Core/Geometry/GeometryCuttingUtils.cs index 338f7776..b35ac47d 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/GeometryCuttingUtils.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/GeometryCuttingUtils.cs @@ -123,51 +123,5 @@ public static float LinePointDistance(Vector2 v, Vector2 w, Vector2 p, out float 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..e0a7199b 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs @@ -1,13 +1,10 @@ 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 @@ -18,7 +15,7 @@ namespace Vim.Format.Geometry /// public class MeshHash { - public IMesh Mesh; + public VimMesh Mesh; public float Tolerance; public int NumFaces; public int NumVertices; @@ -32,13 +29,13 @@ public int Round(float f) public 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); @@ -58,87 +55,9 @@ 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(); - } + public static Dictionary> GroupMeshesByHash(this IEnumerable meshes, float tolerance) + => meshes.AsParallel().GroupBy(m => new MeshHash(m, tolerance)).ToDictionary(grp => grp.Key, grp => grp.ToList()); } } diff --git a/src/cs/vim/Vim.Format.Core/Geometry/PerimeterProjection.cs b/src/cs/vim/Vim.Format.Core/Geometry/PerimeterProjection.cs index 854e7292..6acbe6b1 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/PerimeterProjection.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/PerimeterProjection.cs @@ -2,76 +2,14 @@ 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) + public static List> GeneratePerimeter(this VimMesh mesh, Vector3 planeNormal, float degenerateSegmentEpsilon = 10.0f, float edgeLoopThreshold = 1e-6f) { var q = GetClusterRotation(planeNormal.Normalize(), Vector3.UnitZ); @@ -367,13 +305,13 @@ public int GetHashCode(Tuple obj) => obj.Item1.GetHashCode() ^ obj.Item2.GetHashCode(); } - public static Dictionary, int> BuildEdgeDictionary(this IMesh mesh) + public static Dictionary, int> BuildEdgeDictionary(this VimMesh mesh) { var edges = new Dictionary, int>(new EdgeEqualityComparer()); - var indices = mesh.Indices; + var indices = mesh.indices; - for (var i = 0; i < indices.Count; i += 3) + for (var i = 0; i < indices.Length; i += 3) { var i0 = indices[i + 0]; var i1 = indices[i + 1]; @@ -387,14 +325,14 @@ public static Dictionary, int> BuildEdgeDictionary(this IMesh me return edges; } - public static List> BuildBoundarySegments(this IMesh mesh, Dictionary, int> edgeDictionary, Func transform) + public static List> BuildBoundarySegments(this VimMesh mesh, Dictionary, int> edgeDictionary, Func transform) { var segments = new List>(); - var indices = mesh.Indices; - var vertices = mesh.Vertices; + var indices = mesh.indices; + var vertices = mesh.vertices; - for (var i = 0; i < indices.Count; i += 3) + for (var i = 0; i < indices.Length; i += 3) { var i0 = indices[i + 0]; var i1 = indices[i + 1]; diff --git a/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs b/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs index 4a0e5a63..ae24622f 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,127 +8,77 @@ 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 + public static VimMesh CreateCube() { - 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); - } + 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 CubeFaceted + public static VimMesh CreateCube(AABox box) { - get - { - var cube = Cube; - return cube.Indices.Select(i => cube.Vertices[i]).TriMesh(cube.Indices.Count.Range()); - } + return CreateCube().Scale(box.Extent).Translate(box.Center); } - 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( + private static float Sqrt2 = 2.0f.Sqrt(); + public static VimMesh CreateTetrahedron() + { + var vertices = new[] + { 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)); + new Vector3(0.0f, -1f, 1f / Sqrt2) + }; + var indices = new[] { 0, 1, 2, 1, 0, 3, 0, 2, 3, 1, 3, 2 }; + return new VimMesh(indices, vertices); + } + public static VimMesh CreateSquare() + { + var vertices = new[] + { + new Vector3(-0.5f, -0.5f, 0f), + new Vector3(-0.5f, 0.5f, 0f), + new Vector3(0.5f, 0.5f, 0f), + new Vector3(0.5f, -0.5f, 0f) + }; + + var indices = new[] { 0, 1, 2, 2, 3, 0 }; + + return new VimMesh(indices, vertices); + } // see: https://github.com/mrdoob/three.js/blob/9ef27d1af7809fa4d9943f8d4c4644e365ab6d2d/src/geometries/TorusBufferGeometry.js#L52 public static Vector3 TorusFunction(Vector2 uv, float radius, float tube) @@ -142,7 +90,7 @@ 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 @@ -152,25 +100,17 @@ public static Vector3 SphereFunction(Vector2 uv, float radius) (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) + public static VimMesh 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 Vector2[] CirclePoints(float radius, int numPoints) + => CirclePoints(numPoints).Select(x => x * radius).ToArray(); - public static IArray CirclePoints(int numPoints) - => numPoints.Select(i => CirclePoint(i, numPoints)); + public static Vector2[] CirclePoints(int numPoints) + => Enumerable.Range(0, numPoints).Select(i => CirclePoint(i, numPoints)).ToArray(); public static Vector2 CirclePoint(int i, int numPoints) => new Vector2((i * (Math3d.Constants.TwoPi / numPoints)).Cos(), (i * (Math3d.Constants.TwoPi / numPoints)).Sin()); @@ -178,7 +118,7 @@ public static Vector2 CirclePoint(int i, int numPoints) /// /// Computes the indices of a quad mesh astrip. /// - public static IArray ComputeQuadMeshStripIndices(int usegs, int vsegs, bool wrapUSegs = false, bool wrapVSegs = false) + public static int[] ComputeQuadMeshStripIndices(int usegs, int vsegs, bool wrapUSegs = false, bool wrapVSegs = false) { var indices = new List(); @@ -202,14 +142,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 IArray QuadMeshStripIndicesFromPointRows( + public static int[] QuadMeshStripIndicesFromPointRows( int numPointRows, int numPointsPerRow, bool clockwise = false) @@ -265,10 +205,10 @@ public static IArray QuadMeshStripIndicesFromPointRows( } } - return indices.ToIArray(); + return indices.ToArray(); } - public static IArray TriMeshCylinderCapIndices(int numEdgeVertices) + public static int[] TriMeshCylinderCapIndices(int numEdgeVertices) { // Example cap where numEdgeVertices is 6: // @@ -308,19 +248,19 @@ public static IArray TriMeshCylinderCapIndices(int numEdgeVertices) indices.Add(lastTriangleIndex1); indices.Add(lastTriangleIndex2); - return indices.ToIArray(); + return indices.ToArray(); } /// /// Creates a quad mesh given a mapping from 2 space to 3 space /// - public static IMesh QuadMesh(this Func f, int segs) + public static VimMesh 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 +275,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 index 5ccca143..897ba16d 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/Validation.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/Validation.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using Vim.BFast; -using Vim.G3d; -using Vim.LinqArray; +using Vim.BFastLib; namespace Vim.Format.Geometry { @@ -10,21 +7,21 @@ public static class Validation { public static void ValidateTableRows(this Document doc) { - foreach (var et in doc.EntityTables.Values.ToArray()) + foreach (var et in doc.EntityTables.Values) { - foreach (var c in et.IndexColumns.Values.ToArray()) + foreach (var c in et.IndexColumns.Values) { 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.Values) { 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.Values) { 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}"); @@ -34,9 +31,9 @@ public static void ValidateTableRows(this Document doc) public static void ValidateIndexColumns(this Document doc) { - foreach (var et in doc.EntityTables.Values.ToArray()) + foreach (var et in doc.EntityTables.Values) { - foreach (var ic in et.IndexColumns.Values.ToEnumerable()) + foreach (var ic in et.IndexColumns.Values) { var table = ic.GetRelatedTable(doc); if (table == null) @@ -45,37 +42,9 @@ public static void ValidateIndexColumns(this Document doc) } } - 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()) + foreach (var asset in doc.Assets.Values) AssetInfo.Parse(asset.Name); // This will throw if it fails to parse. } @@ -83,24 +52,7 @@ 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 index 4799219f..f2fc1ba8 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/VimMaterial.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/VimMaterial.cs @@ -1,4 +1,6 @@ -using Vim.G3d; +using System; +using System.Collections.Generic; +using Vim.G3d; using Vim.Math3d; namespace Vim.Format.Geometry @@ -12,10 +14,24 @@ public interface IMaterial 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; + public G3dVim g3d; + public int index; + + public static IEnumerable FromG3d(G3dVim g3d) + { + for(var i =0; i < g3d.GetMaterialCount(); i++) + { + yield return new VimMaterial(g3d, i); + } + } + + public Vector4 Color => g3d.MaterialColors[index]; + public float Smoothness => g3d.MaterialSmoothness[index]; + public float Glossiness => g3d.MaterialGlossiness[index]; + public VimMaterial(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..57830276 --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/Geometry/VimMesh.cs @@ -0,0 +1,466 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Vim.G3d; +using Vim.Math3d; +using static Vim.Format.DocumentBuilder; + +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: submeshIndexOffsets, + submeshMaterials: submeshMaterials, + 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 CloneShallow() + { + var mesh = new VimMesh( + indices, + vertices, + submeshIndexOffsets, + submeshMaterials + ); + return mesh; + } + + public VimMesh CloneDeep() + { + var mesh = new VimMesh( + indices.ToArray(), + vertices.ToArray(), + submeshIndexOffsets.ToArray(), + submeshMaterials.ToArray() + ); + return mesh; + } + + public VimMesh Transform(Matrix4x4 mat) + { + var mesh = CloneShallow(); + + 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); + } + + /// + /// Return true if this mesh indices are sequence equal to the given mesh. + /// + 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 void Validate() + { + //TODO: Validate better + 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}"); + } + } + + /// + /// Reverses triangle winding order of this mesh. + /// + public void ReverseWindingOrder() + { + for (var i = 0; i < indices.Length; i += 3) + { + var tmp = indices[i + 2]; + indices[i + 2] = indices[i]; + indices[i] = tmp; + // indices[i + 1] stays the same + } + } + + + public int[] GetFaceMaterials() + { + // 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 j = 0; + var result = new int[indices.Length / 3]; + for (var s = 0; s < SubmeshCount; s++) + { + var count = submeshIndexCounts[s] / 3; + var mat = submeshMaterials[s]; + for (var i = 0; i < count; i++) + { + result[j++] = mat; + } + } + return result; + } + + /// + /// Merges this mesh and all provided meshes as a new mesh. + /// + public VimMesh Merge(params VimMesh[] others) + { + var meshes = Enumerable.Empty() + .Append(this) + .Concat(others) + .ToArray(); + + return MergeAll(meshes); + } + + /// + /// Merges all provided meshes as one new mesh. + /// + public static VimMesh MergeAll(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; + } + + public VimMesh Unindex() + { + var v = indices.Select(i => vertices[i]); + return new VimMesh(v.ToArray()); + } + + } + + public static class MeshExtensions + { + public static IEnumerable GetAllMeshes(this G3dVim g3d) + { + return Enumerable.Range(0, g3d.GetMeshCount()).Select(i => VimMesh.FromG3d(g3d, i)); + } + + public static VimMesh GetMesh(this G3dVim g3d, int index) + { + return VimMesh.FromG3d(g3d, index); + } + + public 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)); + } + + public static VimMesh Unindex(this VimMesh mesh) + { + var vertices = mesh.indices.Select(i => mesh.vertices[i]); + return new VimMesh(vertices.ToArray()); + } + + public 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 (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(); + } + + 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; + } + + public static VimMesh PickSubmeshes(this VimMesh mesh, IList submeshes) + { + // 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/VimShape.cs b/src/cs/vim/Vim.Format.Core/Geometry/VimShape.cs new file mode 100644 index 00000000..f789981e --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/Geometry/VimShape.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using Vim.G3d; +using Vim.Math3d; + +namespace Vim.Format.Geometry +{ + public class VimShape + { + 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 VimShape(g3d, i); + } + } + + public VimShape(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..4b86268c --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/SerializableEntityTable.cs @@ -0,0 +1,129 @@ +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 ColumnNames + => IndexColumns.Select(c => c.Name) + .Concat(StringColumns.Select(c => c.Name)) + .Concat(DataColumns.Select(c => c.Name)); + + public IEnumerable AllColumns + => IndexColumns + .Concat(StringColumns) + .Concat(DataColumns); + + private readonly static 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 = SerializableEntityTable.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.AsArray()); + } + return bfast; + } + } +} diff --git a/src/cs/vim/Vim.Format.Core/SerializableHeader.cs b/src/cs/vim/Vim.Format.Core/SerializableHeader.cs index c1c3a97c..c2e9010b 100644 --- a/src/cs/vim/Vim.Format.Core/SerializableHeader.cs +++ b/src/cs/vim/Vim.Format.Core/SerializableHeader.cs @@ -1,7 +1,10 @@ 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 @@ -9,8 +12,8 @@ namespace Vim.Format public class SerializableHeader { public static readonly SerializableVersion CurrentVimFormatVersion = VimFormatVersion.Current; - - public const string FormatVersionField = "vim"; + + protected const string FormatVersionField = "vim"; public const string IdField = "id"; public const string RevisionField = "revision"; public const string GeneratorField = "generator"; @@ -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; @@ -273,5 +300,10 @@ public static string CreateDummyPersistingId() /// public string PersistingId => CreatePersistingId(Id, Revision); + + public byte[] ToBytes() + { + return ToString().ToBytesUtf8(); + } } } 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 63c9bdaf..00000000 --- a/src/cs/vim/Vim.Format.Core/Serializer.cs +++ /dev/null @@ -1,321 +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) - { - foreach (var entityTableBufferReader in entitiesBufferReader.Seek().GetBFastBufferReaders()) - { - yield return entityTableBufferReader.ReadEntityTable(schemaOnly); - } - } - - /// - /// Enumerates the SerializableEntityTables contained in the given VIM file. - /// - public static IEnumerable EnumerateEntityTables(this FileInfo vimFileInfo, bool schemaOnly) - { - using (var stream = vimFileInfo.OpenRead()) - { - var entitiesBufferReader = stream.GetBFastBufferReaders(b => b.Name == BufferNames.Entities).FirstOrDefault(); - if (entitiesBufferReader == null) - yield break; - - foreach (var entityTable in entitiesBufferReader.EnumerateEntityTables(schemaOnly)) - { - 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/Validation.cs b/src/cs/vim/Vim.Format.Core/Validation.cs index bb5287ab..6275bf17 100644 --- a/src/cs/vim/Vim.Format.Core/Validation.cs +++ b/src/cs/vim/Vim.Format.Core/Validation.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using Vim.BFast; -using Vim.G3d; -using Vim.LinqArray; +using Vim.BFastLib; namespace Vim.Format { @@ -10,21 +7,21 @@ public static class Validation { public static void ValidateTableRows(this Document doc) { - foreach (var et in doc.EntityTables.Values.ToArray()) + foreach (var et in doc.EntityTables.Values) { - foreach (var c in et.IndexColumns.Values.ToArray()) + foreach (var c in et.IndexColumns.Values) { 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.Values) { 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.Values) { 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}"); @@ -34,9 +31,9 @@ public static void ValidateTableRows(this Document doc) public static void ValidateIndexColumns(this Document doc) { - foreach (var et in doc.EntityTables.Values.ToArray()) + foreach (var et in doc.EntityTables.Values) { - foreach (var ic in et.IndexColumns.Values.ToEnumerable()) + foreach (var ic in et.IndexColumns.Values) { var table = ic.GetRelatedTable(doc); if (table == null) @@ -45,37 +42,9 @@ public static void ValidateIndexColumns(this Document doc) } } - 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()) + 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/VimSchema.cs b/src/cs/vim/Vim.Format.Core/VimSchema.cs index 7790a17b..cc262a08 100644 --- a/src/cs/vim/Vim.Format.Core/VimSchema.cs +++ b/src/cs/vim/Vim.Format.Core/VimSchema.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using Vim.Util; -using Vim.LinqArray; namespace Vim.Format { @@ -45,17 +44,20 @@ public EntityTableSchema AddEntityTableSchema(string entityTableName) } 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(doc.ToDocument()); + + 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.EntityTables.Values) { var ets = vimSchema.AddEntityTableSchema(entityTable.Name); // Collect all the column names in the entity table and sort them alphabetically. - foreach (var columnName in entityTable.Columns.Select(nb => nb.Name).ToEnumerable().OrderBy(n => n)) + foreach (var columnName in entityTable.Columns.Select(nb => nb.Name).OrderBy(n => n)) ets.AddColumn(columnName); } return vimSchema; diff --git a/src/cs/vim/Vim.Format.Tests/FormatTests.cs b/src/cs/vim/Vim.Format.Tests/FormatTests.cs index eb7cb0ed..2ea981a3 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 { @@ -29,17 +29,6 @@ public static void AssertNameAndSizesAreEqual(SerializableEntityTable t1, Serial Assert.AreEqual(t1.StringColumns.Count, t2.StringColumns.Count); for (var i = 0; i < t1.StringColumns.Count; ++i) AssertNameAndSizesAreEqual(t1.StringColumns[i], t2.StringColumns[i]); - - /* Can't expect the numerical values to be precise, because of non-determinism of parallelism when generating string table. - for (var i=0; i < t1.Properties.Length; ++i) - { - var p1 = t1.Properties[i]; - var p2 = t2.Properties[i]; - Assert.AreEqual(p1.EntityId, p2.EntityId); - Assert.AreEqual(p1.Name, p2.Name); - Assert.AreEqual(p1.Value, p2.Value); - } - */ } public static void AssertEquals(SerializableDocument d1, SerializableDocument d2, bool compareStringTables = true) @@ -68,12 +57,12 @@ 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.DataColumns.Keys.OrderBy(n => n).ToArray(), et2.DataColumns.Keys.OrderBy(n => n).ToArray()); + Assert.AreEqual(et1.IndexColumns.Keys.OrderBy(n => n).ToArray(), et2.IndexColumns.Keys.OrderBy(n => n).ToArray()); + Assert.AreEqual(et1.StringColumns.Keys.OrderBy(n => n).ToArray(), et2.StringColumns.Keys.OrderBy(n => n).ToArray()); - var columns1 = et1.Columns.ToEnumerable().OrderBy(c => c.Name).ToArray(); - var columns2 = et2.Columns.ToEnumerable().OrderBy(c => c.Name).ToArray(); + var columns1 = et1.Columns.OrderBy(c => c.Name).ToArray(); + var columns2 = et2.Columns.OrderBy(c => c.Name).ToArray(); Assert.AreEqual( columns1.Select(ec => ec.Name).ToArray(), @@ -104,9 +93,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.DataColumns.Keys, et2.DataColumns.Keys)); + Assert.IsTrue(IsSupersetOf(et1.IndexColumns.Keys, et2.IndexColumns.Keys)); + Assert.IsTrue(IsSupersetOf(et1.StringColumns.Keys, et2.StringColumns.Keys)); var columns1 = et1.Columns.ToArray(); var columns2 = et2.Columns.ToArray(); @@ -131,15 +120,15 @@ 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 = VimSchema.Create(d1._Document); + var schema2 = VimSchema.Create(d2._Document); Assert.IsTrue(VimSchema.IsSuperSetOf(schema1, schema2)); var etKeys1 = d1.EntityTables.Keys; var etKeys2 = d2.EntityTables.Keys; - Assert.IsTrue(IsSupersetOf(etKeys1.ToEnumerable(), etKeys2.ToEnumerable())); + Assert.IsTrue(IsSupersetOf(etKeys1, etKeys2)); - foreach (var key in etKeys2.ToEnumerable()) + foreach (var key in etKeys2) { if (skipGeometryAndNodes && key.ToLowerInvariant().Contains("geometry")) continue; @@ -154,12 +143,12 @@ 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 = VimSchema.Create(d1._Document); + var schema2 = VimSchema.Create(d2._Document); 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.EntityTables.Keys.OrderBy(n => n).ToArray(); + var entityTables2 = d2.EntityTables.Keys.OrderBy(n => n).ToArray(); Assert.AreEqual(entityTables1, entityTables2); foreach (var k in entityTables1) diff --git a/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs b/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs deleted file mode 100644 index 6cf7f142..00000000 --- a/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs +++ /dev/null @@ -1,318 +0,0 @@ -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 = { - 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) - { - 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++) - { - 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) - { - g.Validate(); - foreach (var attr in g.Attributes.ToEnumerable()) - Console.WriteLine($"{attr.Descriptor} elementCount={attr.ElementCount}"); - } - - 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() - { - var nMesh = 0; - foreach (var g in AllMeshes) - { - Console.WriteLine($"Testing mesh {nMesh++}"); - g.Validate(); - //ValidateGeometry(g.ToTriMesh()); - } - - 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.IsTrue(XYTriangle.Planar()); - Assert.AreEqual(new[] { 0, 1, 2 }, XYTriangle.Indices.ToArray()); - - Assert.AreEqual(3, XYQuad.NumCornersPerFace); - Assert.AreEqual(2, XYQuad.NumFaces); - Assert.AreEqual(4, XYQuad.Vertices.Count); - Assert.AreEqual(6, XYQuad.Indices.Count); - - Assert.IsTrue(XYQuad.Planar()); - 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(3, XYQuad2x2.NumCornersPerFace); - Assert.AreEqual(8, XYQuad2x2.NumFaces); - Assert.AreEqual(9, XYQuad2x2.Vertices.Count); - Assert.AreEqual(24, XYQuad2x2.Indices.Count); - - 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(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.IsTrue(XYTriangleTwice.Planar()); - Assert.AreEqual(new[] { 0, 1, 2, 3, 4, 5 }, XYTriangleTwice.Indices.ToArray()); - } - - [Test] - public static void BasicManipulationTests() - { - foreach (var g in AllMeshes) - GeometryNullOps(g); - } - - [Test] - public static void OutputGeometryData() - { - var n = 0; - foreach (var g in AllMeshes) - { - Console.WriteLine($"Geometry {n++}"); - for (var i = 0; i < g.Vertices.Count && i < 10; ++i) - { - Console.WriteLine($"Vertex {i} {g.Vertices[i]}"); - } - - if (g.Vertices.Count > 10) - { - var last = g.Vertices.Count - 1; - Console.WriteLine("..."); - Console.WriteLine($"Vertex {last} {g.Vertices[last]}"); - } - - for (var i = 0; i < g.NumFaces && i < 10; ++i) - { - Console.WriteLine($"Face {i}: {g.Triangle(i)}"); - } - - if (g.Vertices.Count > 10) - { - var last = g.NumFaces - 1; - Console.WriteLine("..."); - Console.WriteLine($"Face {last}: {g.Triangle(last)}"); - } - } - } - - [Test] - public static void StripIndicesTests() - { - var emptyStrip00 = Primitives.QuadMeshStripIndicesFromPointRows(0, 0); - Assert.AreEqual(0, emptyStrip00.Count); - - var emptyStrip01 = Primitives.QuadMeshStripIndicesFromPointRows(0, 1); - Assert.AreEqual(0, emptyStrip01.Count); - - var emptyStrip10 = Primitives.QuadMeshStripIndicesFromPointRows(1, 0); - Assert.AreEqual(0, emptyStrip10.Count); - - var emptyStrip11 = Primitives.QuadMeshStripIndicesFromPointRows(1, 1); - Assert.AreEqual(0, emptyStrip11.Count); - - var emptyStrip12 = Primitives.QuadMeshStripIndicesFromPointRows(1, 2); - Assert.AreEqual(0, emptyStrip12.Count); - - var emptyStrip21 = Primitives.QuadMeshStripIndicesFromPointRows(2, 1); - Assert.AreEqual(0, emptyStrip21.Count); - - // COUNTER-CLOCKWISE TEST (DEFAULT) - // 2------3 <--- row 1: [2,3] - // | | => counter-clockwise quad: (0,1,3,2) - // | | - // 0------1 <--- row 0: [0,1] - var strip22 = Primitives.QuadMeshStripIndicesFromPointRows(2, 2); - Assert.AreEqual(4, strip22.Count); - Assert.AreEqual(0, strip22[0]); - Assert.AreEqual(1, strip22[1]); - Assert.AreEqual(3, strip22[2]); - Assert.AreEqual(2, strip22[3]); - - // CLOCKWISE TEST - // 2------3 <--- row 1: [2,3] - // | | => clockwise quad: (2,3,1,0) - // | | - // 0------1 <--- row 0: [0,1] - var clockwiseStrip22 = Primitives.QuadMeshStripIndicesFromPointRows(2, 2, true); - Assert.AreEqual(4, clockwiseStrip22.Count); - Assert.AreEqual(2, clockwiseStrip22[0]); - Assert.AreEqual(3, clockwiseStrip22[1]); - Assert.AreEqual(1, clockwiseStrip22[2]); - Assert.AreEqual(0, clockwiseStrip22[3]); - var reversed22 = clockwiseStrip22.Reverse(); - for (var i = 0; i < strip22.Count; ++i) - { - Assert.AreEqual(strip22[i], reversed22[i]); - } - - // *------*------* - // | | | - // | | | - // *------*------* - var strip23 = Primitives.QuadMeshStripIndicesFromPointRows(2, 3); - Assert.AreEqual(4 * 2, strip23.Count); - - // *------*------*------* - // | | | | - // | | | | - // *------*------*------* - // | | | | - // | | | | - // *------*------*------* - var strip34 = Primitives.QuadMeshStripIndicesFromPointRows(3, 4); - Assert.AreEqual(4 * 6, strip34.Count); - } - - [Test] - public static void TriangleSerializationTest() - { - // Serialize a triangle g3d to a bfast 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 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()); - } - } -} diff --git a/src/cs/vim/Vim.Format.Tests/Geometry/MeshStripsTest.cs b/src/cs/vim/Vim.Format.Tests/Geometry/MeshStripsTest.cs new file mode 100644 index 00000000..bd8b0a0f --- /dev/null +++ b/src/cs/vim/Vim.Format.Tests/Geometry/MeshStripsTest.cs @@ -0,0 +1,119 @@ +using NUnit.Framework; +using System; +using System.Linq; +using Vim.Format.Geometry; + +namespace Vim.Format.Tests.Geometry +{ + internal class MeshStripsTest + { + [Test] + public static void Strip_0_0() + { + var emptyStrip00 = Primitives.QuadMeshStripIndicesFromPointRows(0, 0); + Assert.AreEqual(0, emptyStrip00.Length); + } + + [Test] + public static void Strip_0_1() + { + var emptyStrip01 = Primitives.QuadMeshStripIndicesFromPointRows(0, 1); + Assert.AreEqual(0, emptyStrip01.Length); + } + + [Test] + public static void Strip_1_0() + { + var emptyStrip10 = Primitives.QuadMeshStripIndicesFromPointRows(1, 0); + Assert.AreEqual(0, emptyStrip10.Length); + } + + [Test] + public static void Strip_1_1() + { + var emptyStrip11 = Primitives.QuadMeshStripIndicesFromPointRows(1, 1); + Assert.AreEqual(0, emptyStrip11.Length); + } + + [Test] + public static void Strip_1_2() + { + var emptyStrip12 = Primitives.QuadMeshStripIndicesFromPointRows(1, 2); + Assert.AreEqual(0, emptyStrip12.Length); + } + + [Test] + public static void Strip_2_1() + { + var emptyStrip21 = Primitives.QuadMeshStripIndicesFromPointRows(2, 1); + Assert.AreEqual(0, emptyStrip21.Length); + } + + [Test] + public static void Strip_2_2_CounterClockwise() + { + // COUNTER-CLOCKWISE TEST (DEFAULT) + // 2------3 <--- row 1: [2,3] + // | | => counter-clockwise quad: (0,1,3,2) + // | | + // 0------1 <--- row 0: [0,1] + var strip22 = Primitives.QuadMeshStripIndicesFromPointRows(2, 2); + Assert.AreEqual(4, strip22.Length); + Assert.AreEqual(0, strip22[0]); + Assert.AreEqual(1, strip22[1]); + Assert.AreEqual(3, strip22[2]); + Assert.AreEqual(2, strip22[3]); + } + + [Test] + public static void Strip_2_2_Clockwise() + { + // CLOCKWISE TEST + // 2------3 <--- row 1: [2,3] + // | | => clockwise quad: (2,3,1,0) + // | | + // 0------1 <--- row 0: [0,1] + var clockwiseStrip22 = Primitives.QuadMeshStripIndicesFromPointRows(2, 2, true); + 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]); + clockwiseStrip22.Reverse(); + } + + [Test] + public static void Strip_2_2_Reverse() + { + var clockwiseStrip22 = Primitives.QuadMeshStripIndicesFromPointRows(2, 2, true); + var strip22 = Primitives.QuadMeshStripIndicesFromPointRows(2, 2); + + Assert.IsTrue(clockwiseStrip22.Reverse().SequenceEqual(strip22)); + } + + [Test] + public static void Strip_2_3() + { + // *------*------* + // | | | + // | | | + // *------*------* + var strip23 = Primitives.QuadMeshStripIndicesFromPointRows(2, 3); + Assert.AreEqual(4 * 2, strip23.Length); + } + + [Test] + public static void Strip_3_4() + { + // *------*------*------* + // | | | | + // | | | | + // *------*------*------* + // | | | | + // | | | | + // *------*------*------* + var strip34 = Primitives.QuadMeshStripIndicesFromPointRows(3, 4); + Assert.AreEqual(4 * 6, strip34.Length); + } + } +} diff --git a/src/cs/vim/Vim.Format.Tests/Geometry/TestShapes.cs b/src/cs/vim/Vim.Format.Tests/Geometry/TestShapes.cs new file mode 100644 index 00000000..2a1f4d9d --- /dev/null +++ b/src/cs/vim/Vim.Format.Tests/Geometry/TestShapes.cs @@ -0,0 +1,114 @@ +using NUnit.Framework; +using System.Linq; +using System; +using Vim.Format.Geometry; +using Vim.Math3d; + +namespace Vim.Format.Tests.Geometry +{ + public class TestShapes + { + 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.Merge(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 + XYTriangleTwice, // 7 + }; + + public static float SmallTolerance = 0.0001f; + + [Test] + public void Test_Triangle() + { + Assert.AreEqual(3, XYTriangle.NumCornersPerFace); + Assert.AreEqual(1, XYTriangle.NumFaces); + Assert.AreEqual(3, XYTriangle.vertices.Length); + Assert.AreEqual(3, XYTriangle.indices.Length); + Assert.AreEqual(1, XYTriangle.Triangles().Length); + Assert.IsTrue(XYTriangle.Planar(SmallTolerance)); + Assert.AreEqual(new[] { 0, 1, 2 }, XYTriangle.indices); + } + + [Test] + public void Test_Quad() + { + Assert.AreEqual(3, XYQuad.NumCornersPerFace); + Assert.AreEqual(2, XYQuad.NumFaces); + Assert.AreEqual(4, XYQuad.vertices.Length); + Assert.AreEqual(6, XYQuad.indices.Length); + Assert.IsTrue(TestShapes.XYQuad.Planar(SmallTolerance)); + Assert.AreEqual(new[] { 0, 1, 2, 0, 2, 3 }, XYQuad.indices); + } + + [Test] + public void Test_QuadFromFunc() + { + Assert.AreEqual(3, XYQuadFromFunc.NumCornersPerFace); + Assert.AreEqual(2, XYQuadFromFunc.NumFaces); + Assert.AreEqual(4, XYQuadFromFunc.vertices.Length); + Assert.AreEqual(6, XYQuadFromFunc.indices.Length); + } + + [Test] + public void Test_Quad2x2() + { + Assert.AreEqual(3, XYQuad2x2.NumCornersPerFace); + Assert.AreEqual(8, XYQuad2x2.NumFaces); + Assert.AreEqual(9, XYQuad2x2.vertices.Length); + Assert.AreEqual(24, XYQuad2x2.indices.Length); + } + + + [Test] + public void Test_Tetrahedron() + { + Assert.AreEqual(3, Tetrahedron.NumCornersPerFace); + Assert.AreEqual(4, Tetrahedron.NumFaces); + Assert.AreEqual(4, Tetrahedron.vertices.Length); + Assert.AreEqual(12, Tetrahedron.indices.Length); + } + + [Test] + public void Test_TriangleTwice() + { + Assert.AreEqual(3, XYTriangleTwice.NumCornersPerFace); + Assert.AreEqual(2, XYTriangleTwice.NumFaces); + 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); + } + } +} diff --git a/src/cs/vim/Vim.Format.Tests/Geometry/TestTransformations.cs b/src/cs/vim/Vim.Format.Tests/Geometry/TestTransformations.cs new file mode 100644 index 00000000..bd93e37e --- /dev/null +++ b/src/cs/vim/Vim.Format.Tests/Geometry/TestTransformations.cs @@ -0,0 +1,49 @@ +using NUnit.Framework; +using System; +using System.Linq; +using Vim.Format.Geometry; +using Vim.G3d; +using Vim.Math3d; + +namespace Vim.Format.Tests.Geometry +{ + public static class TestTransformations + { + [Test] + public static void IdentityOperations() + { + foreach (var g in TestShapes.AllMeshes) + { + g.GeometryEquals(g); + g.Translate(Vector3.Zero).GeometryEquals(g); + g.Scale(Vector3.Zero).GeometryEquals(g); + g.Transform(Matrix4x4.Identity).GeometryEquals(g); + } + } + + [Test] + public static void ReverseWindingOrder_InvertsTriangle() + { + var t1 = TestShapes.XYTriangle; + var t2 = TestShapes.XYTriangle; + t1.ReverseWindingOrder(); + Assert.That(t1.indices.SequenceEqual(t2.indices.Reverse())); + } + + [Test] + public static void SaveLoad_AllMeshes() + { + foreach (var g in TestShapes.AllMeshes) + { + var g3d = g.ToG3d(); + var bfast = g3d.ToBFast(); + var g3d2 = new G3dVim(bfast); + var result = VimMesh.FromG3d(g3d2); + Assert.IsTrue(g.GeometryEquals(result)); + } + } + + + + } +} diff --git a/src/cs/vim/Vim.Format.Tests/Geometry/VimMeshTests.cs b/src/cs/vim/Vim.Format.Tests/Geometry/VimMeshTests.cs new file mode 100644 index 00000000..16286fb4 --- /dev/null +++ b/src/cs/vim/Vim.Format.Tests/Geometry/VimMeshTests.cs @@ -0,0 +1,16 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Vim.Format.Tests.Geometry +{ + [TestFixture] + internal class VimMeshTests + { + + + } +} 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..075c41e5 --- /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 IMesh; + // 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_ToIMesh() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + // var mesh = scene.Document.Geometry as IMesh; + // 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(IMesh 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, VimMaterial 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/Vim.Format.Tests.csproj b/src/cs/vim/Vim.Format.Tests/Vim.Format.Tests.csproj index 6a85c322..3f9308b8 100644 --- a/src/cs/vim/Vim.Format.Tests/Vim.Format.Tests.csproj +++ b/src/cs/vim/Vim.Format.Tests/Vim.Format.Tests.csproj @@ -16,9 +16,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..62663db9 --- /dev/null +++ b/src/cs/vim/Vim.Format.Vimx.Conversion/VimxConverter.cs @@ -0,0 +1,225 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Vim.BFastLib; +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/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 c66023cf..7b45e3ff 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 { @@ -40,22 +39,22 @@ public static DictionaryOfLists GetAssetsInViewOrderedByViewIn public static string GetBimDocumentFileName(this DocumentModel dm, int bimDocumentIndex) => Path.GetFileName(dm.GetBimDocumentPathName(bimDocumentIndex)); - public static IArray GetBimDocumentDisplayUnits(this DocumentModel dm, BimDocument bd) + public static DisplayUnit[] GetBimDocumentDisplayUnits(this DocumentModel dm, BimDocument bd) => dm.DisplayUnitInBimDocumentList .Where(item => item.BimDocument.Index == bd.Index) .Select(item => item.DisplayUnit) - .ToIArray(); + .ToArray(); - public static IArray GetBimDocumentPhases(this DocumentModel dm, BimDocument bd) + public static Phase[] GetBimDocumentPhases(this DocumentModel dm, BimDocument bd) => dm.PhaseOrderInBimDocumentList .Where(item => item.BimDocument.Index == bd.Index) .Select(item => item.Phase) - .ToIArray(); + .ToArray(); public const string LengthSpecLegacyPrefix = "UT_Length"; public const string LengthSpecPrefix = "autodesk.spec.aec:length"; - public static DisplayUnit GetLengthDisplayUnit(this IArray displayUnits) + public static DisplayUnit GetLengthDisplayUnit(this DisplayUnit[] displayUnits) => displayUnits.FirstOrDefault(du => { var spec = du.Spec; diff --git a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs index 1a176ad1..23c723df 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs @@ -4,8 +4,8 @@ using System.Collections.Generic; using System.Linq; using Vim.Math3d; -using Vim.LinqArray; using Vim.Format.ObjectModel; +using Vim.Util; namespace Vim.Format.ObjectModel { // AUTO-GENERATED @@ -1888,10 +1888,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; @@ -1907,14 +1907,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; @@ -1932,26 +1932,26 @@ 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 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; @@ -1975,14 +1975,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; @@ -2000,48 +2000,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; @@ -2076,24 +2076,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; @@ -2116,18 +2116,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; @@ -2147,18 +2147,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; @@ -2178,12 +2178,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; @@ -2200,16 +2200,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; @@ -2228,10 +2228,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; @@ -2247,26 +2247,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; @@ -2290,72 +2290,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; @@ -2402,12 +2402,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; @@ -2424,14 +2424,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; @@ -2449,26 +2449,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; @@ -2492,20 +2492,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; @@ -2526,16 +2526,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; @@ -2554,64 +2554,64 @@ 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 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; @@ -2654,60 +2654,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; @@ -2748,12 +2748,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; @@ -2770,12 +2770,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; @@ -2792,12 +2792,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; @@ -2814,12 +2814,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; @@ -2836,24 +2836,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; @@ -2876,26 +2876,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; @@ -2919,48 +2919,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; @@ -2995,18 +2995,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; @@ -3026,18 +3026,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; @@ -3057,12 +3057,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; @@ -3079,10 +3079,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; @@ -3098,24 +3098,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; @@ -3138,10 +3138,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; @@ -3157,10 +3157,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; @@ -3176,12 +3176,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; @@ -3198,14 +3198,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; @@ -3223,14 +3223,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; @@ -3248,16 +3248,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; @@ -3276,12 +3276,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; @@ -3298,24 +3298,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; @@ -3338,18 +3338,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; @@ -3369,38 +3369,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; @@ -3430,20 +3430,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; @@ -3464,12 +3464,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; @@ -3486,10 +3486,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; @@ -3505,14 +3505,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; @@ -3530,14 +3530,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; @@ -3555,10 +3555,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; @@ -3574,12 +3574,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; @@ -3596,12 +3596,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; @@ -3618,12 +3618,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; @@ -3640,12 +3640,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; @@ -3662,20 +3662,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; @@ -3696,18 +3696,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; @@ -3724,60 +3724,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 @@ -3898,389 +3898,389 @@ 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(); - 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(); + 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(); - 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(); + 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/Validation.cs b/src/cs/vim/Vim.Format/ObjectModel/Validation.cs index 24fb6399..14d5d24c 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 { @@ -58,7 +58,7 @@ public static void ValidateBimDocument(this DocumentModel dm, ObjectModelValidat 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) @@ -143,14 +143,14 @@ public static void ValidateAssets(this DocumentModel dm) 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) { - 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,7 +162,7 @@ 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}"); @@ -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)}"); @@ -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); @@ -265,7 +265,7 @@ void ValidateDVector3Domain(string label, DVector3 value, DVector3 lowerInclusiv } public static Dictionary> GetViewToElementsMap( - this IArray elementInViewList) + this ElementInView[] elementInViewList) => elementInViewList .Select(eiv => (viewIndex: eiv._View?.Index ?? -1, elementIndex: eiv._Element?.Index ?? -1)) .GroupBy(t => t.viewIndex) @@ -275,10 +275,10 @@ public static Dictionary> GetViewToElementsMap( public 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; @@ -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]; @@ -316,7 +316,7 @@ public static void ValidateEntitiesWithElement(this DocumentModel dm) public 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/ILoadingStep.cs b/src/cs/vim/Vim.Format/SceneBuilder/ILoadingStep.cs new file mode 100644 index 00000000..6fd58731 --- /dev/null +++ b/src/cs/vim/Vim.Format/SceneBuilder/ILoadingStep.cs @@ -0,0 +1,21 @@ +namespace Vim.Format.SceneBuilder +{ + using System; + + public class LoadingProgress: Progress<(string, double)>, ILoadingProgress + { + public LoadingProgress(Action<(string, double)> handler) : base(handler) + { + } + } + + public interface ILoadingProgress : IProgress<(string, double)> + { + } + + public interface ILoadingStep + { + void Run(ILoadingProgress progress); + float Effort { get; } + } +} diff --git a/src/cs/vim/Vim.Format/SceneBuilder/LoadingProgress.cs b/src/cs/vim/Vim.Format/SceneBuilder/LoadingProgress.cs new file mode 100644 index 00000000..f5847bd6 --- /dev/null +++ b/src/cs/vim/Vim.Format/SceneBuilder/LoadingProgress.cs @@ -0,0 +1,22 @@ +namespace Vim.Format.SceneBuilder +{ + public class CumulativeProgressDecorator : ILoadingProgress + { + private readonly double _total; + private double _current; + + private readonly ILoadingProgress _progress; + + private CumulativeProgressDecorator(ILoadingProgress progress, float total) + => (_progress, _total) = (progress, total); + + public static CumulativeProgressDecorator Decorate(ILoadingProgress logger, float total) + => logger != null ? new CumulativeProgressDecorator(logger, total) : null; + + public void Report((string, double) value) + { + _current += value.Item2; + _progress.Report((value.Item1, _current / _total)); + } + } +} diff --git a/src/cs/vim/Vim.Format/SceneBuilder/LoadingSequence.cs b/src/cs/vim/Vim.Format/SceneBuilder/LoadingSequence.cs new file mode 100644 index 00000000..d864c8f3 --- /dev/null +++ b/src/cs/vim/Vim.Format/SceneBuilder/LoadingSequence.cs @@ -0,0 +1,22 @@ +using System.Linq; + +namespace Vim.Format.SceneBuilder +{ + public class LoadingSequence : ILoadingStep + { + private readonly LoadingStep[] _steps; + public float Effort { get; } + + public LoadingSequence(params LoadingStep[] steps) + { + _steps = steps; + Effort = _steps.Sum(s => s.Effort); + } + + public void Run(ILoadingProgress progress) + { + foreach (var step in _steps) + step.Run(progress); + } + } +} diff --git a/src/cs/vim/Vim.Format/SceneBuilder/LoadingStep.cs b/src/cs/vim/Vim.Format/SceneBuilder/LoadingStep.cs new file mode 100644 index 00000000..d8bbe47d --- /dev/null +++ b/src/cs/vim/Vim.Format/SceneBuilder/LoadingStep.cs @@ -0,0 +1,24 @@ +using System; + +namespace Vim.Format.SceneBuilder +{ + public class LoadingStep : ILoadingStep + { + private readonly Action _action; + private readonly string _name; + public float Effort { get; } + + public LoadingStep(Action action, string name, float effort = 1f) + { + _action = action; + _name = name; + Effort = effort; + } + + public void Run(ILoadingProgress progress) + { + progress?.Report((_name, Effort)); + _action(); + } + } +} diff --git a/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs b/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs index 55dc2b94..d7ce8e52 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 { @@ -23,24 +22,12 @@ public class VimValidationException : Exception { public VimValidationException(string message) : base(message) { } } - - public static void ValidateGeometry(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) + + public static void ValidateDocumentModelToG3dInvariantsNext(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})"); } } } @@ -96,16 +82,15 @@ public static void ValidateDocumentModelToG3dInvariants(this VimScene vim) public 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) { - 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(VimShape)} ({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(VimShape)} {shape.Index}"); + ValidateColorDomain($"{nameof(VimShape)} 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.EntityTables.Keys) { 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..bcafb031 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 VimShape[] Shapes { get; private set; } + public VimMaterial[] 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; @@ -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 = VimShape.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 = VimMaterial.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..962916a6 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 @@ -156,7 +155,7 @@ public static Vector4 GetDiffuseColor(this Material m) public 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); @@ -173,6 +172,6 @@ public static ElementInfo GetElementInfo(this VimScene vim, Element element) => vim.DocumentModel.GetElementInfo(element); public static VimSchema GetVimSchema(this VimScene vim) - => VimSchema.Create(vim.Document); + => VimSchema.Create(vim._SerializableDocument); } } diff --git a/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs b/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs index 9bb7a754..488f1adb 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs @@ -1,58 +1,68 @@ -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 bool HideByDefault - { - get - { - var instanceFlags = (InstanceFlags)_Scene.Document.Geometry.InstanceFlags.ElementAtOrDefault(NodeIndex); - return (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); 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 ca5691f7..db3e19f8 100644 --- a/src/cs/vim/Vim.Format/Vim.Format.csproj +++ b/src/cs/vim/Vim.Format/Vim.Format.csproj @@ -18,12 +18,9 @@ - - - - - - + + +