From edebdb7a41db98eeb40f6f55c552bc6b289c77e9 Mon Sep 17 00:00:00 2001 From: Vadym Vorotilin Date: Fri, 29 Nov 2024 18:13:26 +0200 Subject: [PATCH 01/22] Initial cleanup: removal of unused stuff under Geometry --- .../vim/Vim.Format.Core/Geometry/ArrayOps.cs | 260 ------- .../vim/Vim.Format.Core/Geometry/ArrayOps.tt | 96 --- .../vim/Vim.Format.Core/Geometry/Bounded.cs | 6 - .../Vim.Format.Core/Geometry/CatmullClark.cs | 230 ------ .../Geometry/GeometryCuttingUtils.cs | 173 ----- .../Vim.Format.Core/Geometry/GeometryUtil.cs | 68 -- src/cs/vim/Vim.Format.Core/Geometry/IScene.cs | 42 +- src/cs/vim/Vim.Format.Core/Geometry/KdTree.cs | 2 +- .../Vim.Format.Core/Geometry/MeshDebugView.cs | 21 - .../Geometry/MeshExtensions.cs | 278 -------- .../Geometry/MeshOptimization.cs | 100 +-- .../Geometry/PerimeterProjection.cs | 657 ------------------ .../Vim.Format.Core/Geometry/Primitives.cs | 169 +---- .../Geometry/SceneExtensions.cs | 53 -- .../Vim.Format.Core/Geometry/Serialization.cs | 22 - .../vim/Vim.Format.Core/Geometry/Topology.cs | 236 ------- .../Vim.Format.Core/Geometry/Validation.cs | 106 --- .../Vim.Format.Core/Vim.Format.Core.csproj | 15 - .../Geometry/GeometryTests.cs | 636 ++++++++--------- .../Geometry/PerimeterTest.cs | 27 - src/cs/vim/Vim.Format/Merge/MergeService.cs | 4 +- .../vim/Vim.Format/SceneBuilder/Validation.cs | 14 + .../Vim.Format/SceneBuilder/VimSceneNode.cs | 1 - 23 files changed, 343 insertions(+), 2873 deletions(-) delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/ArrayOps.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/ArrayOps.tt delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/CatmullClark.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/GeometryCuttingUtils.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/GeometryUtil.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/MeshDebugView.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/PerimeterProjection.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/Serialization.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/Topology.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/Validation.cs delete mode 100644 src/cs/vim/Vim.Format.Tests/Geometry/PerimeterTest.cs 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/Bounded.cs b/src/cs/vim/Vim.Format.Core/Geometry/Bounded.cs index ca2571de..51079b50 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/Bounded.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/Bounded.cs @@ -6,10 +6,4 @@ public interface IBounded { AABox Bounds { get; } } - - public static class Bounded - { - public static AABox UpdateBounds(this IBounded self, AABox box) - => box.Merge(self.Bounds); - } } diff --git a/src/cs/vim/Vim.Format.Core/Geometry/CatmullClark.cs b/src/cs/vim/Vim.Format.Core/Geometry/CatmullClark.cs deleted file mode 100644 index 4ffbd54a..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/CatmullClark.cs +++ /dev/null @@ -1,230 +0,0 @@ -using System; -using System.Collections.Generic; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - // This is used to index an edge from any two vertices. - // Vertex order doesnt matter. - public class GeometryEdgeKey : IEquatable - { - public int Vertex0 { get; } - public int Vertex1 { get; } - - public GeometryEdgeKey(int v0, int v1) - { - Vertex0 = Math.Min(v0, v1); - Vertex1 = Math.Max(v0, v1); - } - - public override int GetHashCode() - => Hash.Combine(Vertex0.GetHashCode(), Vertex1.GetHashCode()); - - public override bool Equals(object obj) - => obj is GeometryEdgeKey && Equals((GeometryEdgeKey)obj); - - public bool Equals(GeometryEdgeKey p) - => Vertex0 == p.Vertex0 && Vertex1 == p.Vertex1; - } - - // Stores information about an edge in a mesh. - public class GeometryEdge - { - public Vector3 EdgePoint { get; set; } - public int EdgePointIndex { get; set; } = -1; - public int Face0 { get; set; } = -1; - public int Face1 { get; set; } = -1; - } - - public static class CatmullClarkSmoothing - { - public static Dictionary CreateEdgeMap(this IMesh geometry) - { - var globalEdgeIndex = 0; - var edgeMap = new Dictionary(); - var currentIndex = 0; - for (var faceIndex = 0; faceIndex < geometry.NumFaces; faceIndex++) - { - var faceSize = 3; - for (var edgeIndex = 0; edgeIndex < faceSize; edgeIndex++) - { - var geometryEdgeKey = new GeometryEdgeKey(geometry.Indices[currentIndex + edgeIndex], geometry.Indices[currentIndex + (edgeIndex + 1) % faceSize]); - if (edgeMap.ContainsKey(geometryEdgeKey)) - { - edgeMap[geometryEdgeKey].Face1 = faceIndex; - } - else - { - var geometryEdge = new GeometryEdge(); - geometryEdge.EdgePointIndex = globalEdgeIndex++; - geometryEdge.Face0 = faceIndex; - edgeMap[geometryEdgeKey] = geometryEdge; - } - } - currentIndex += faceSize; - } - - return edgeMap; - } - - public static IMesh CatmullClark(this IMesh geometry, float smoothing = 0.0f) - { - var edgeMap = geometry.CreateEdgeMap(); - - var numQuads = geometry.NumFaces * 3; - var numVertices = geometry.Vertices.Count + edgeMap.Count + geometry.NumFaces; - - var facePoints = new Vector3[geometry.NumFaces]; - var vertexFPoints = new Vector3[geometry.Vertices.Count]; - var vertexRPoints = new Vector3[geometry.Vertices.Count]; - var vertexNumFaces = new float[geometry.Vertices.Count]; - var vertexNumEdges = new float[geometry.Vertices.Count]; - var newVertices = new Vector3[numVertices]; - var newIndices = new int[numQuads * 4]; - var edgeVertices = new bool[geometry.Vertices.Count]; - - foreach (var edge in edgeMap) - { - if (edge.Value.Face0 == -1 || edge.Value.Face1 == -1) - { - edgeVertices[edge.Key.Vertex0] = true; - edgeVertices[edge.Key.Vertex1] = true; - } - } - - // For each face, add a face point - // Set each face point to be the average of all original points for the respective face. - // For each edge, add an edge point. - // Set each edge point to be the average of the two neighbouring face points and its two original endpoints. - // For each face point, add an edge for every edge of the face, connecting the face point to each edge point for the face. - // For each original point P, take the average F of all n (recently created) face points for faces touching P, and take - // the average R of all n edge midpoints for (original) edges touching P, where each edge midpoint is the average of its - // two endpoint vertices (not to be confused with new "edge points" above). Move each original point to the point - // - // (F + 2 R + ( n − 3 ) P) / n - // - // This is the barycenter of P, R and F with respective weights (n − 3), 2 and 1. - // - // Connect each new vertex point to the new edge points of all original edges incident on the original vertex. - // Define new faces as enclosed by edges. - - CalculateFaceCentroidPoints(geometry, facePoints, vertexNumFaces, vertexFPoints); - CalculateEdgePoints(geometry, edgeMap, facePoints, vertexNumEdges, vertexRPoints, smoothing); - CalculateVertexPoints(geometry, vertexNumFaces, vertexFPoints, vertexNumEdges, vertexRPoints, newVertices, edgeVertices, smoothing); - - var facePointStartIndex = geometry.Vertices.Count; - var edgePointStartIndex = facePointStartIndex + facePoints.Length; - - // Copy all points into a single buffer - for (var i = 0; i < facePoints.Length; i++) - { - newVertices[facePointStartIndex + i] = facePoints[i]; - } - foreach (var edge in edgeMap) - { - newVertices[edgePointStartIndex + edge.Value.EdgePointIndex] = edge.Value.EdgePoint; - } - - return GenerateMesh(geometry, newVertices, newIndices, edgeMap, facePointStartIndex, edgePointStartIndex, numQuads); - } - - public static void CalculateFaceCentroidPoints(IMesh geometry, Vector3[] outFacePoints, float[] outVertexNumFaces, Vector3[] outVertexFPoints) - { - var currentVertexIndex = 0; - for (var faceIndex = 0; faceIndex < geometry.NumFaces; faceIndex++) - { - var faceSize = geometry.NumCornersPerFace; - - var facePoint = new Vector3(); - - for (var edgeIndex = 0; edgeIndex < faceSize; edgeIndex++) - { - var vertexIndex = geometry.Indices[currentVertexIndex + edgeIndex]; - facePoint += geometry.Vertices[vertexIndex]; - } - - facePoint /= faceSize; - - outFacePoints[faceIndex] = facePoint; - - for (var edgeIndex = 0; edgeIndex < faceSize; edgeIndex++) - { - var vertexIndex = geometry.Indices[currentVertexIndex + edgeIndex]; - outVertexNumFaces[vertexIndex]++; - outVertexFPoints[vertexIndex] += facePoint; - } - - currentVertexIndex += faceSize; - } - } - - public static void CalculateEdgePoints(IMesh geometry, Dictionary edgeMap, Vector3[] facePoints, float[] outVertexNumEdges, Vector3[] outVertexRPoints, float smoothing) - { - foreach (var edge in edgeMap) - { - var n = 2.0f; - var middlePoint = geometry.Vertices[edge.Key.Vertex0] + geometry.Vertices[edge.Key.Vertex1]; - var edgePoint = middlePoint; - - if (edge.Value.Face0 >= 0 && edge.Value.Face1 >= 0) - { - edgePoint += facePoints[edge.Value.Face0]; - edgePoint += facePoints[edge.Value.Face1]; - n += 2.0f; - } - - middlePoint /= 2.0f; - edgePoint /= n; - - edge.Value.EdgePoint = edgePoint * smoothing + middlePoint * (1.0f - smoothing); - - outVertexNumEdges[edge.Key.Vertex0]++; - outVertexNumEdges[edge.Key.Vertex1]++; - outVertexRPoints[edge.Key.Vertex0] += edge.Value.EdgePoint; - outVertexRPoints[edge.Key.Vertex1] += edge.Value.EdgePoint; - } - } - - public static void CalculateVertexPoints(IMesh geometry, float[] vertexNumFaces, Vector3[] vertexFPoints, float[] vertexNumEdges, Vector3[] vertexRPoints, Vector3[] outNewVertices, bool[] edgeVertices, float smoothing) - { - for (var index = 0; index < geometry.Vertices.Count; index++) - { - var numFaces = vertexNumFaces[index]; - var numEdges = vertexNumEdges[index]; - var newVertex = (vertexFPoints[index] / numFaces + 2.0f * vertexRPoints[index] / numEdges + (numFaces - 3.0f) * geometry.Vertices[index]) / numFaces; - - outNewVertices[index] = edgeVertices[index] ? geometry.Vertices[index] : smoothing * newVertex + (1.0f - smoothing) * geometry.Vertices[index]; - } - } - - public static IMesh GenerateMesh(IMesh geometry, Vector3[] newVertices, int[] newIndices, Dictionary edgeMap, int facePointStartIndex, int edgePointStartIndex, int numQuads) - { - var currentNewVertexIndex = 0; - var currentOldVertexIndex = 0; - for (var faceIndex = 0; faceIndex < geometry.NumFaces; faceIndex++) - { - var faceSize = geometry.NumCornersPerFace; - - for (var i = 0; i < faceSize; i++) - { - var prevFaceVertexIndex = geometry.Indices[currentOldVertexIndex + i]; - var faceVertexIndex = geometry.Indices[currentOldVertexIndex + (i + 1) % faceSize]; - var nextFaceVertexIndex = geometry.Indices[currentOldVertexIndex + (i + 2) % faceSize]; - - var edgeKey0 = new GeometryEdgeKey(prevFaceVertexIndex, faceVertexIndex); - var edgeKey1 = new GeometryEdgeKey(faceVertexIndex, nextFaceVertexIndex); - - newIndices[currentNewVertexIndex++] = faceIndex + facePointStartIndex; - newIndices[currentNewVertexIndex++] = edgeMap[edgeKey0].EdgePointIndex + edgePointStartIndex; - newIndices[currentNewVertexIndex++] = faceVertexIndex; - newIndices[currentNewVertexIndex++] = edgeMap[edgeKey1].EdgePointIndex + edgePointStartIndex; - } - - currentOldVertexIndex += faceSize; - } - - return Primitives.QuadMesh(newVertices.ToIArray(), newIndices.ToIArray()); - } - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/GeometryCuttingUtils.cs b/src/cs/vim/Vim.Format.Core/Geometry/GeometryCuttingUtils.cs deleted file mode 100644 index 338f7776..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/GeometryCuttingUtils.cs +++ /dev/null @@ -1,173 +0,0 @@ -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - public static class GeometryCuttingUtils - { - // Fins the intersection between two lines. - // Returns true if they intersect - // t and u are the distances of the intersection point along the two line - public static bool LineLineIntersection(Vector2 line1Origin, Vector2 line1Direction, Vector2 line2Origin, Vector2 line2Direction, out float t, out float u, float epsilon = 0.01f) - { - var line1P2 = line1Origin + line1Direction; - var line2P2 = line2Origin + line2Direction; - - var x1 = line1Origin.X; - var y1 = line1Origin.Y; - var x2 = line1P2.X; - var y2 = line1P2.Y; - var x3 = line2Origin.X; - var y3 = line2Origin.Y; - var x4 = line2P2.X; - var y4 = line2P2.Y; - - var denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); - - if (denominator.Abs() < epsilon) - { - t = 0.0f; - u = 0.0f; - return false; - } - - var tNeumerator = (x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4); - var uNeumerator = (x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3); - - t = tNeumerator / denominator; - u = -uNeumerator / denominator; - - return true; - } - - // Returns the distance between two lines - // t and u are the distances if the intersection points along the two lines - public static float LineLineDistance(Vector2 line1A, Vector2 line1B, Vector2 line2A, Vector2 line2B, out float t, out float u, float epsilon = 0.0000001f) - { - var x1 = line1A.X; - var y1 = line1A.Y; - var x2 = line1B.X; - var y2 = line1B.Y; - var x3 = line2A.X; - var y3 = line2A.Y; - var x4 = line2B.X; - var y4 = line2B.Y; - - var denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); - - if (denominator.Abs() >= epsilon) - { - // Lines are not parallel, they should intersect nicely - var tNeumerator = (x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4); - var uNeumerator = (x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3); - - t = tNeumerator / denominator; - u = -uNeumerator / denominator; - - var e = 0.0; - if (t >= -e && t <= 1.0 + e && u >= -e && u <= 1.0 + e) - { - t = t.Clamp(0.0f, 1.0f); - u = u.Clamp(0.0f, 1.0f); - return 0; - } - } - - // Parallel or non intersecting lines - default to point to line checks - - u = 0.0f; - var minDistance = LinePointDistance(line1A, line1B, line2A, out t); - var distance = LinePointDistance(line1A, line1B, line2B, out var closestPoint); - if (distance < minDistance) - { - minDistance = distance; - t = closestPoint; - u = 1.0f; - } - - distance = LinePointDistance(line2A, line2B, line1A, out closestPoint); - if (distance < minDistance) - { - minDistance = distance; - u = closestPoint; - t = 0.0f; - } - - distance = LinePointDistance(line2A, line2B, line1B, out closestPoint); - if (distance < minDistance) - { - minDistance = distance; - u = closestPoint; - t = 1.0f; - } - - return minDistance; - } - - // Returns the distance between a line and a point. - // t is the distance along the line of the closest point - public static float LinePointDistance(Vector2 v, Vector2 w, Vector2 p, out float t) - { - // Return minimum distance between line segment vw and point p - var l2 = (v - w).LengthSquared(); // i.e. |w-v|^2 - avoid a sqrt - if (l2 == 0.0f) // v == w case - { - t = 0.5f; - return (p - v).Length(); - } - - // Consider the line extending the segment, parameterized as v + t (w - v). - // We find projection of point p onto the line. - // It falls where t = [(p-v) . (w-v)] / |w-v|^2 - // We clamp t from [0,1] to handle points outside the segment vw. - t = ((p - v).Dot(w - v) / l2).Clamp(0.0f, 1.0f); - var closestPoint = v + t * (w - v); // Projection falls on the segment - return (p - closestPoint).Length(); - } - - - - // Apply a surface function that projects a 2d triangulation in UV space into a 3d triangulation - /* public static IGeometry ApplySurfaceFunction(this List triangulations, Func surfaceFunction) - { - var allIndices = new List(); - var allVertices = new List(); - - foreach (var triangulation in triangulations) - { - var vertices = triangulation.Points.ToList(); - var indices = triangulation.TriangleIndices; - - while (true) - { - List outIndices; - List outVertices; - bool done = SubdivideLargeTriangles(vertices, indices, out outVertices, out outIndices, 0.3f); - - vertices = outVertices; - indices = outIndices; - - if (done) - { - break; - } - } - - // Apply surface function - var vertices3d = vertices.Select(x => surfaceFunction(x.ToVector())); - - // Merge all triangulations - int startIndex = allVertices.Count; - allVertices.AddRange(vertices3d); - allIndices.AddRange(indices.Select(x => x + startIndex)); - } - - var cutGeometry = new G3D( - allVertices.ToIArray().ToVertexAttribute(), - allIndices.ToIArray().ToIndexAttribute(), - new FunctionalArray(allIndices.Count / 3, x => 3).ToFaceSizeAttribute() - ).ToIMesh(); - - return cutGeometry; - }*/ - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/GeometryUtil.cs b/src/cs/vim/Vim.Format.Core/Geometry/GeometryUtil.cs deleted file mode 100644 index db8d33e7..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/GeometryUtil.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Vim.LinqArray; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - // TODO: many of these functions should live in other places - public static class GeometryUtil - { - public static IArray Normalize(this IArray vectors) - => vectors.Select(v => v.Normalize()); - - public static bool SequenceAlmostEquals(this IArray vs1, IArray vs2, float tolerance = Math3d.Constants.Tolerance) - => vs1.Count == vs2.Count && vs1.Indices().All(i => vs1[i].AlmostEquals(vs2[i], tolerance)); - - public static bool AreColinear(this IEnumerable vectors, Vector3 reference, float tolerance = (float)Math3d.Constants.OneTenthOfADegree) - => !reference.IsNaN() && vectors.All(v => v.Colinear(reference, tolerance)); - - public static bool AreColinear(this IEnumerable vectors, float tolerance = (float)Math3d.Constants.OneTenthOfADegree) - => vectors.ToList().AreColinear(tolerance); - - public static bool AreColinear(this IList vectors, float tolerance = (float)Math3d.Constants.OneTenthOfADegree) - => vectors.Count <= 1 || vectors.Skip(1).AreColinear(vectors[0], tolerance); - - public static AABox BoundingBox(this IArray vertices) - => AABox.Create(vertices.ToEnumerable()); - - public static IArray SampleZeroToOneInclusive(this int count) - => (count + 1).Select(i => i / (float)count); - - public static IArray SampleZeroToOneExclusive(this int count) - => count.Select(i => i / (float)count); - - public static IArray InterpolateInclusive(this int count, Func function) - => count.SampleZeroToOneInclusive().Select(function); - - public static IArray Interpolate(this Line self, int count) - => count.InterpolateInclusive(self.Lerp); - - public static IArray Rotate(this IArray self, Vector3 axis, float angle) - => self.Transform(Matrix4x4.CreateFromAxisAngle(axis, angle)); - - public static IArray Transform(this IArray self, Matrix4x4 matrix) - => self.Select(x => x.Transform(matrix)); - - public static Int3 Sort(this Int3 v) - { - if (v.X < v.Y) - { - return (v.Y < v.Z) - ? new Int3(v.X, v.Y, v.Z) - : (v.X < v.Z) - ? new Int3(v.X, v.Z, v.Y) - : new Int3(v.Z, v.X, v.Y); - } - else - { - return (v.X < v.Z) - ? new Int3(v.Y, v.X, v.Z) - : (v.Y < v.Z) - ? new Int3(v.Y, v.Z, v.X) - : new Int3(v.Z, v.Y, v.X); - } - } - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/IScene.cs b/src/cs/vim/Vim.Format.Core/Geometry/IScene.cs index eb2e5821..3f7acff6 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/IScene.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/IScene.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using Vim.LinqArray; +using Vim.LinqArray; using Vim.Math3d; namespace Vim.Format.Geometry @@ -24,44 +23,5 @@ public interface ISceneNode 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/KdTree.cs b/src/cs/vim/Vim.Format.Core/Geometry/KdTree.cs index 153e1a31..fb03edf0 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/KdTree.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/KdTree.cs @@ -34,7 +34,7 @@ public void Add(T item) { if (IsSplit) throw new Exception("Cannot add meshes after split"); - Box = item.UpdateBounds(Box); + Box = Box.Merge(item.Bounds); Items.Add(item); } 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 index 506cb3d3..a6e3ac6e 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/MeshExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/MeshExtensions.cs @@ -4,13 +4,11 @@ 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(); @@ -30,31 +28,6 @@ public static IMesh ToIMesh(this IEnumerable self) 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) { @@ -70,28 +43,12 @@ public static IArray GetFaceMaterials(this IMesh mesh) .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; @@ -177,22 +134,6 @@ public static IMesh Merge(this IMesh mesh, params IMesh[] others) }); } - 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) @@ -200,26 +141,6 @@ public static bool GeometryEquals(this IMesh mesh, IMesh other, float tolerance 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; @@ -233,126 +154,12 @@ public static IGeometryAttributes ReverseWindingOrder(this IMesh mesh) 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]); @@ -364,90 +171,5 @@ public static Triangle Triangle(this IMesh mesh, int 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..000bd2e3 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs @@ -1,9 +1,4 @@ -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.CompilerServices; -using Vim.G3d; -using Vim.LinqArray; +using Vim.LinqArray; using Vim.Math3d; namespace Vim.Format.Geometry @@ -20,11 +15,11 @@ public class MeshHash { public IMesh Mesh; public float Tolerance; - public int NumFaces; - public int NumVertices; - public int TopologyHash; - public Int3 BoxExtents; - public Int3 BoxMin; + public readonly int NumFaces; + public readonly int NumVertices; + public readonly int TopologyHash; + public readonly Int3 BoxExtents; + public readonly Int3 BoxMin; public int Round(float f) => (int)(f / Tolerance); @@ -58,87 +53,4 @@ public override int GetHashCode() => Hash.Combine(NumFaces, NumVertices, TopologyHash, BoxMin.GetHashCode(), BoxExtents.GetHashCode()); } - public class IntegerPositionColorNormal - { - public Int3 Position; - public Int3 Color; - public Int3 Normal; - - public IntegerPositionColorNormal(Int3 pos, Int3 color, Int3 normal) - => (Position, Color, Normal) = (pos, color, normal); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - => Hash.Combine(Position.GetHashCode(), Color.GetHashCode(), Normal.GetHashCode()); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) - => Equals(obj as IntegerPositionColorNormal); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(IntegerPositionColorNormal other) - => other != null && Position.Equals(other.Position) && Color.Equals(other.Color) && Normal.Equals(other.Normal); - } - - public static class Optimization - { - public static Dictionary> GroupMeshesByHash(this IArray meshes, float tolerance) - => meshes.ToEnumerable().GroupMeshesByHash(tolerance); - - public static Dictionary> GroupMeshesByHash(this IEnumerable meshes, float tolerance) - => meshes.AsParallel().GroupBy(m => new MeshHash(m, tolerance)).ToDictionary(grp => grp.Key, grp => grp.ToList()); - - /// - /// Merges vertices that are within a certain distance and have similar normals and colors. - /// - public static IMesh WeldVertices(this IMesh g, float threshold = (float)Math3d.Constants.MmToFeet) - { - var positions = g.Vertices; - var normals = g.GetOrComputeVertexNormals().ToArray(); - var colors = g.VertexColors ?? Vector4.Zero.Repeat(positions.Count); - - // Vertex indices by color, and then by normal - var d = new Dictionary(); - - // The mapping of old indices to new ones - var indexRemap = new int[g.Vertices.Count]; - - // This is a list of vertex indices that we are keeping - var vertRemap = new List(); - - // Local helper function - Int3 ToInt3(Vector3 v) - => new Int3((int)v.X, (int)v.Y, (int)v.Z); - - for (var i = 0; i < g.NumVertices; ++i) - { - var p = ToInt3(positions[i] * (1 / threshold)); - var c = ToInt3(colors[i].ToVector3() * 10000); - var n = ToInt3(normals[i] * 10000); - - var pcn = new IntegerPositionColorNormal(p, c, n); - - if (d.TryGetValue(pcn, out var index)) - { - indexRemap[i] = index; - continue; - } - - var newVertIndex = vertRemap.Count; - indexRemap[i] = newVertIndex; - vertRemap.Add(i); - - d.Add(pcn, newVertIndex); - } - - Debug.Assert(vertRemap.Count <= g.NumVertices); - for (var i = 1; i < vertRemap.Count; ++i) - Debug.Assert(vertRemap[i - 1] < vertRemap[i]); - - return g.RemapVertices( - vertRemap.ToIArray(), - g.Indices.Select(i => indexRemap[i])) - .ToIMesh(); - } - } } diff --git a/src/cs/vim/Vim.Format.Core/Geometry/PerimeterProjection.cs b/src/cs/vim/Vim.Format.Core/Geometry/PerimeterProjection.cs deleted file mode 100644 index 854e7292..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/PerimeterProjection.cs +++ /dev/null @@ -1,657 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Vim.Util; -using Vim.LinqArray; -using Vim.Math3d; -using LineApprox = System.Tuple; - -namespace Vim.Format.Geometry -{ - public static class PerimeterProjection - { - public static Int2 ToXYApproximation(this Vector3 v, float tolerance) - => new Int2((int)(v.X / tolerance), (int)(v.Y / tolerance)); - - public static Vector2 FromXYApproximation(this Int2 v, float tolerance) - => new Vector2(v.X * tolerance, v.Y * tolerance); - - public static LineApprox ToXYApproximation(this Line line, float tolerance) - => Tuple.Create(line.A.ToXYApproximation(tolerance), line.B.ToXYApproximation(tolerance)); - - public static double Angle(this Int2 v) - => Math.Atan2(v.Y, v.X); - - public static double Angle(this Int2 a, Int2 b) - => (a - b).Angle(); - - public static IEnumerable PerimeterXY(this IMesh mesh, float tolerance = 0.001f) - { - var srcLines = mesh.GetAllEdgesAsLines(); - var approxLines = srcLines.Select(line => line.ToXYApproximation(tolerance)); - var lineSet = new HashSet(approxLines.ToArrayInParallel()); - Debug.WriteLine($"Went from {srcLines.Count} to {lineSet.Count}"); - var d = new DictionaryOfLists(); - foreach (var ab in lineSet) - { - d.Add(ab.Item1, ab); - d.Add(ab.Item2, ab); - } - var r = new List(); - if (d.Count == 0) - return r; - - var firstKey = d.Keys.Minimize(int.MaxValue, ab => ab.X.Min(ab.Y)); - var currentKey = firstKey; - var prevAngle = 0.0; - - // If we can't find the point in the dictionary we have completed - while (d.ContainsKey(currentKey)) - { - // Find the candidate points; - var candidates = d[currentKey].Select(line => line.Item1 == currentKey ? line.Item2 : line.Item1); - - // Find the best match by maximizing angle - var bestMatch = candidates.Maximize(0.0, c => currentKey.Angle(c) - prevAngle); - - // Update the return set - r.Add(bestMatch.FromXYApproximation(tolerance)); - - // Now save the angle for the next stage. - prevAngle = currentKey.Angle(bestMatch); - - // Remove this key from the dictionary - d.Remove(currentKey); - - // Now we are at a new point - currentKey = bestMatch; - } - - return r; - } - - public static List> GeneratePerimeter(this IMesh mesh, Vector3 planeNormal, float degenerateSegmentEpsilon = 10.0f, float edgeLoopThreshold = 1e-6f) - { - var q = GetClusterRotation(planeNormal.Normalize(), Vector3.UnitZ); - - var segments = mesh.BuildBoundarySegments(null, - input => input.Transform(q).ToVector2()); - - segments = segments.SplitIntersectingCurves(edgeLoopThreshold * 50); - - var graph = segments.BuildConnectionGraph(edgeLoopThreshold * 50); - var loops = graph.FindGraphLoops(edgeLoopThreshold * 50); - - loops = loops.RemoveDuplicatedVertices(edgeLoopThreshold * 50); - - while (true) - { - var newLoops = loops.RemoveDegenerateSegmentsLooping(edgeLoopThreshold); - if (newLoops.Count == loops.Count) - { - var notEqual = false; - for (var i = 0; i < newLoops.Count; i++) - { - if (newLoops[i].Count != loops[i].Count) - { - notEqual = true; - break; - } - } - - if (!notEqual) - { - break; - } - } - - loops = newLoops; - } - - return loops; - } - - public static bool IsNaN(this Quaternion q) - => q.X.IsNaN() || q.Y.IsNaN() || q.Z.IsNaN() || q.W.IsNaN(); - - // Finds all of the intersections between the curves and splits them - // Also adds a pseudo intersection at the left-most point, this gives the FindGraphLoops - // algorithm a good starting point - public static List> SplitIntersectingCurves(this List> curves, float threshold) - { - var thresholdSquared = threshold * threshold; - var curveList = new LinkedList>(curves); - - var intersections = new Dictionary, List>>>(); // Madness in great ones must not unwatched go. - var leftMostPoint = Vector2.MaxValue; - List leftMostCurve = null; - var leftMostIntersectionPoint = 0.0f; - - foreach (var currentCurveA in curveList) - { - for (var i = 0; i < currentCurveA.Count; i++) - { - var point = currentCurveA[i]; - if (point.X < leftMostPoint.X || (point.X == leftMostPoint.X && point.Y < leftMostPoint.Y)) - { - leftMostPoint = point; - leftMostIntersectionPoint = i; - leftMostCurve = currentCurveA; - } - } - - foreach (var currentCurveB in curveList) - { - if (currentCurveA != currentCurveB) - { - float t, u; - var distance = IntersectCurves(currentCurveA, currentCurveB, out t, out u); - if (distance < threshold) - { - if (!intersections.ContainsKey(currentCurveA)) - { - intersections[currentCurveA] = new List>>(); - } - if (!intersections.ContainsKey(currentCurveB)) - { - intersections[currentCurveB] = new List>>(); - } - - intersections[currentCurveA].Add(new Tuple>(distance, t, currentCurveB)); - intersections[currentCurveB].Add(new Tuple>(distance, u, currentCurveA)); - } - } - } - } - - // split the left most curve so that FindGraphLoops has a good starting point - if (leftMostIntersectionPoint != 0.0 && leftMostIntersectionPoint != leftMostCurve.Count - 1) - { - intersections[leftMostCurve].Add(new Tuple>(0.0f, leftMostIntersectionPoint, null)); - } - - var edgeLoops = new List>(); - foreach (var intersection in intersections) - { - var splits = intersection.Value.Select(x => x.Item2); - - edgeLoops.AddRange(intersection.Key.SplitCurve(splits)); - } - - return edgeLoops; - } - - // Cuts the curve between min and max and returns the part in between - public static List> SplitCurve(this List curve, IEnumerable splits) - { - var result = new List>(); - if (splits.Count() == 0) - { - result.Add(curve); - return result; - } - - splits = splits.OrderBy(x => x).ToList(); - var newSplits = new List(); - if (splits.First() != 0.0f) - { - newSplits.Add(0.0f); - } - - newSplits.AddRange(splits); - - if (splits.Last() < curve.Count - 1) - { - newSplits.Add(curve.Count - 1); - } - - for (var i = 0; i < newSplits.Count - 1; i++) - { - var min = newSplits[i]; - var max = newSplits[i + 1]; - - var clampedCurve = curve.ClampCurve(min, max); - - if (clampedCurve.CurveLength() > 1e-5f) - { - result.Add(clampedCurve); - } - } - - return result; - } - - - // Cuts the curve between min and max and returns the part in between - public static List ClampCurve(this List curve, float min, float max) - { - var floorMin = (float)Math.Floor(min); - var floorMax = (float)Math.Floor(max); - var fracMin = min - floorMin; - var fracMax = max - floorMax; - - if (fracMin == 0.0 && floorMin > 0) - { - floorMin--; - fracMin++; - } - if (fracMax == 0.0 && floorMax > 0) - { - floorMax--; - fracMax++; - } - - var indexMin = (int)floorMin; - var indexMax = (int)floorMax; - - var result = new List(); - if (fracMin != 1.0) - { - result.Add(curve[indexMin].Lerp(curve[indexMin + 1], (float)fracMin)); - } - for (var i = indexMin + 1; i <= indexMax; i++) - { - result.Add(curve[i]); - } - result.Add(curve[indexMax].Lerp(curve[indexMax + 1], (float)fracMax)); - - return result; - } - - // Returns the length of the curve - public static float CurveLength(this List curve) - { - var curveLength = 0.0f; - for (var x = 0; x < curve.Count - 1; x++) - { - curveLength += (curve[x] - curve[x + 1]).Length(); - } - - return curveLength; - } - - // Returns min distance between piecewise linear curves A and B. - // t = distance along curve A - // u = distance along curve B - // TODO: Return multiple intersections within a tolerance - public static float IntersectCurves(List segmentsA, List segmentsB, out float t, out float u) - { - var minDistance = float.MaxValue; - t = 0.0f; - u = 0.0f; - - for (var segmentAIndex = 0; segmentAIndex < segmentsA.Count - 1; segmentAIndex++) - { - var v1a = segmentsA[segmentAIndex]; - var v1b = segmentsA[segmentAIndex + 1]; - - for (var segmentBIndex = 0; segmentBIndex < segmentsB.Count - 1; segmentBIndex++) - { - var v2a = segmentsB[segmentBIndex]; - var v2b = segmentsB[segmentBIndex + 1]; - - float fracT, fracU; - var distance = GeometryCuttingUtils.LineLineDistance(v1a, v1b, v2a, v2b, out fracT, out fracU); - - if (distance < minDistance) - { - minDistance = distance; - t = segmentAIndex + fracT; - u = segmentBIndex + fracU; - } - } - } - - return minDistance; - } - - public static List> RemoveDuplicatedVertices(this List> edgeLoops, float threshold) - { - var thresholdSquared = threshold * threshold; - foreach (var edgeLoop in edgeLoops) - { - for (var i = 0; i < edgeLoop.Count; i++) - { - if ((edgeLoop[i] - edgeLoop[(i + 1) % edgeLoop.Count]).LengthSquared() <= thresholdSquared) - { - edgeLoop.RemoveAt(i); - i--; - } - } - } - - return edgeLoops; - } - - public static Quaternion GetClusterRotation(Vector3 clusterNormal, Vector3 alignTo, float tolerance = 0.001f) - { - var c = clusterNormal.Cross(alignTo); - var d = clusterNormal.Dot(alignTo); - - if (d >= 1f - tolerance) - return Quaternion.Identity; - - if (d <= -(1f - tolerance)) - { - // Need an axis of rotation that is perpendicular to the two input vectors - var axis = clusterNormal.Cross(alignTo.Dot(Vector3.UnitY) > 0.999f ? Vector3.UnitZ : Vector3.UnitY); - return Quaternion.CreateFromAxisAngle(axis, (float)Math.PI); - } - - var s = (float)Math.Sqrt((1.0f + d) * 2.0f); - var invs = 1.0f / s; - - var q = new Quaternion(c * invs, s * 0.5f); - q = q.Normalize(); - - Debug.Assert(!q.IsNaN()); - return q; - } - - public static void AddEdgeToDictionary(Dictionary, int> d, int x, int y) - { - var key = new Tuple(x, y); - if (!d.ContainsKey(key)) - d[key] = 1; - else - d[key]++; - } - - private class EdgeEqualityComparer : IEqualityComparer> - { - public bool Equals(Tuple x, Tuple y) - => ((x.Item1 == y.Item1) && (x.Item2 == y.Item2)) || ((x.Item1 == y.Item2) && (x.Item2 == y.Item1)); - - public int GetHashCode(Tuple obj) - => obj.Item1.GetHashCode() ^ obj.Item2.GetHashCode(); - } - - public static Dictionary, int> BuildEdgeDictionary(this IMesh mesh) - { - var edges = new Dictionary, int>(new EdgeEqualityComparer()); - - var indices = mesh.Indices; - - for (var i = 0; i < indices.Count; i += 3) - { - var i0 = indices[i + 0]; - var i1 = indices[i + 1]; - var i2 = indices[i + 2]; - - AddEdgeToDictionary(edges, i0, i1); - AddEdgeToDictionary(edges, i1, i2); - AddEdgeToDictionary(edges, i2, i0); - } - - return edges; - } - - public static List> BuildBoundarySegments(this IMesh mesh, Dictionary, int> edgeDictionary, Func transform) - { - var segments = new List>(); - - var indices = mesh.Indices; - var vertices = mesh.Vertices; - - for (var i = 0; i < indices.Count; i += 3) - { - var i0 = indices[i + 0]; - var i1 = indices[i + 1]; - var i2 = indices[i + 2]; - - var v0 = transform(vertices[i0]); - var v1 = transform(vertices[i1]); - var v2 = transform(vertices[i2]); - - if (edgeDictionary == null || edgeDictionary[new Tuple(i0, i1)] != 2) segments.Add(new List { v0, v1 }); - if (edgeDictionary == null || edgeDictionary[new Tuple(i1, i2)] != 2) segments.Add(new List { v1, v2 }); - if (edgeDictionary == null || edgeDictionary[new Tuple(i2, i0)] != 2) segments.Add(new List { v2, v0 }); - } - - return segments; - } - - // Builds a node connection graph from a list of curves - public static Dictionary, List>>> BuildConnectionGraph(this List> curves, float threshold) - { - var connectionGraph = new Dictionary, List>>>(); - var reverseCurves = new Dictionary, List>(); - var thresholdSquared = threshold * threshold; - - var pointList = new Vector2[curves.Count]; - for (var i = 0; i < curves.Count; i++) - { - var curve = curves[i]; - connectionGraph[curve.First()] = new List, List>>(); - connectionGraph[curve.Last()] = new List, List>>(); - - var reverseCurve = new List(curve); - reverseCurve.Reverse(); - reverseCurves[curve] = reverseCurve; - reverseCurves[reverseCurve] = curve; - } - - foreach (var point in connectionGraph) - { - foreach (var curve in curves) - { - var distanceSquared = (curve.First() - point.Key).LengthSquared(); - if (distanceSquared <= thresholdSquared) - { - point.Value.Add(new Tuple, List>(curve, reverseCurves[curve])); - } - - distanceSquared = (curve.Last() - point.Key).LengthSquared(); - if (distanceSquared <= thresholdSquared) - { - point.Value.Add(new Tuple, List>(reverseCurves[curve], curve)); - } - } - } - - return connectionGraph; - } - - // Build a loop around the perimeter of the graph - // TODO: Handle multiple loops - public static List> FindGraphLoops(this Dictionary, List>>> connectionGraph, float threshold) - { - // Start from the left most point, build the perimeter by selecting the connection with the minimum clockwise angle - var thresholdSquared = threshold * threshold; - var leftMostPoint = Vector2.MaxValue; - foreach (var item in connectionGraph) - { - if (item.Value.Count < 2) - { - continue; - } - if (item.Key.X < leftMostPoint.X || (item.Key.X == leftMostPoint.X && item.Key.Y < leftMostPoint.Y)) - { - leftMostPoint = item.Key; - } - } - - var visitedNodes = new Dictionary, bool>(); - - var result = new List>(); - var loop = new List(); - var currentPointLast = leftMostPoint + new Vector2(0, 1); - var currentPoint = leftMostPoint; - var previousPoint = currentPoint; - - var segmentCount = 0; - while (true) - { - if (!connectionGraph.ContainsKey(currentPoint)) - { - // Failed :( - result.Add(loop); - return result; - } - - var list = connectionGraph[currentPoint].Where(x => - { - if (connectionGraph[x.Item1.Last()].Count <= 1) - { - // Don't go down a dead end, this might need improving with some kind of backtracking when a dead end was found - return false; - } - - if (visitedNodes.ContainsKey(x.Item1)) - { - return false; - } - - return true; - }); - var sortedList = list.OrderBy(x => - { - var s1 = (currentPoint - currentPointLast).Normalize(); - var s2 = (x.Item1[1] - x.Item1[0]).Normalize(); - var angle = (float)Math.Atan2(s1.Cross(s2), s1.Dot(s2)); - return angle; - }).ToList(); - - if (sortedList.Count == 0) - { - // Failed :( - result.Add(loop); - return result; - } - - var nextConnection = sortedList.First(); - visitedNodes[nextConnection.Item1] = true; - visitedNodes[nextConnection.Item2] = true; - segmentCount++; - loop.AddRange(nextConnection.Item1); - previousPoint = currentPoint; - currentPoint = nextConnection.Item1.Last(); - currentPointLast = nextConnection.Item1[nextConnection.Item1.Count - 2]; - - if ((currentPoint - loop[0]).LengthSquared() < thresholdSquared) - { - result.Add(loop); - break; - } - - if (segmentCount > connectionGraph.Count) - { - // Failed :( - result.Add(loop); - return result; - } - } - - return result; - } - - // Remove degenerate segments - public static List> RemoveDegenerateSegments(this List> edgeLoops, float epsilon = 1e-10f) - { - var result = new List>(); - foreach (var edgeLoop in edgeLoops) - { - if (edgeLoop.Count < 2) - { - //result.Add(edgeLoop); - continue; - } - - var newEdgeLoop = new List(edgeLoop); - for (var i = 1; i < newEdgeLoop.Count - 1; i++) - { - var v0 = newEdgeLoop[i - 1]; - var v1 = newEdgeLoop[i]; - var v2 = newEdgeLoop[i + 1]; - var area = (v1 - v0).Cross(v1 - v2).Abs(); - if (area < epsilon) - { - newEdgeLoop.RemoveAt(i); - i--; - } - } - - if (newEdgeLoop.Count < 2) - { - continue; - } - - result.Add(newEdgeLoop); - } - - return result; - } - public static List> RemoveDegenerateSegmentsLooping(this List> edgeLoops, float epsilon = 1e-10f) - { - var result = new List>(); - foreach (var edgeLoop in edgeLoops) - { - if (edgeLoop.Count < 2) - { - //result.Add(edgeLoop); - continue; - } - - var newEdgeLoop = new List(edgeLoop); - for (var i = 0; i < newEdgeLoop.Count; i++) - { - var v0 = newEdgeLoop[(i - 1 + newEdgeLoop.Count) % newEdgeLoop.Count]; - var v1 = newEdgeLoop[i]; - var v2 = newEdgeLoop[(i + 1) % newEdgeLoop.Count]; - var area = (v1 - v0).Cross(v1 - v2).Abs(); - if (area < epsilon) - { - newEdgeLoop.RemoveAt(i); - i--; - } - } - - if (newEdgeLoop.Count < 2) - { - continue; - } - - result.Add(newEdgeLoop); - } - - return result; - } - - public static List> RemoveDegenerateSegments(this List> edgeLoops, float epsilon, Func faceFunction) - { - var result = new List>(); - foreach (var edgeLoop in edgeLoops) - { - if (edgeLoop.Count < 2) - { - //result.Add(edgeLoop); - continue; - } - - var newEdgeLoop = new List(edgeLoop); - for (var i = 1; i < newEdgeLoop.Count - 1; i++) - { - var v0 = faceFunction(newEdgeLoop[i - 1]); - var v1 = faceFunction(newEdgeLoop[i]); - var v2 = faceFunction(newEdgeLoop[i + 1]); - var area = (v1 - v0).Cross(v1 - v2).LengthSquared(); - if (area < epsilon * epsilon) - { - newEdgeLoop.RemoveAt(i); - i--; - } - } - - if (newEdgeLoop.Count < 2) - { - continue; - } - - result.Add(newEdgeLoop); - } - - return result; - } - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs b/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs index 2e41024c..65d772a1 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Vim.G3d; using Vim.LinqArray; @@ -7,7 +6,6 @@ namespace Vim.Format.Geometry { - // TODO: plane, cylinder, cone, ruled face, public static class Primitives { public static IMesh TriMesh(IEnumerable attributes) @@ -32,29 +30,6 @@ public static IMesh TriMesh( submeshMaterials?.ToSubmeshMaterialAttribute() ); - public static IMesh TriMesh(this IArray vertices, IArray indices = null, params GeometryAttribute[] attributes) - => new GeometryAttribute[] { - vertices?.ToPositionAttribute(), - indices?.ToIndexAttribute(), - }.Concat(attributes).ToIMesh(); - - public static IMesh QuadMesh(params GeometryAttribute[] attributes) - => QuadMesh(attributes.AsEnumerable()); - - public static IMesh QuadMesh(this IEnumerable attributes) - => new QuadMesh(attributes.Where(x => x != null)).ToTriMesh(); - - public static IMesh QuadMesh(this IArray vertices, IArray indices = null, IArray uvs = null, IArray materials = null, IArray objectIds = null) - => QuadMesh( - vertices.ToPositionAttribute(), - indices?.ToIndexAttribute(), - uvs?.ToVertexUvAttribute(), - materials?.ToFaceMaterialAttribute() - ); - - public static IMesh QuadMesh(Vector3 a, Vector3 b, Vector3 c, Vector3 d) - => QuadMesh(new[] { a, b, c, d }.ToIArray()); - public static IMesh Cube { get @@ -106,105 +81,6 @@ public static IMesh CubeFaceted } } - public static IMesh ToIMesh(this AABox box) - => Cube.Scale(box.Extent).Translate(box.Center); - - public static float Sqrt2 = 2.0f.Sqrt(); - - public static readonly IMesh Tetrahedron - = TriMesh(LinqArray.LinqArray.Create( - new Vector3(1f, 0.0f, -1f / Sqrt2), - new Vector3(-1f, 0.0f, -1f / Sqrt2), - new Vector3(0.0f, 1f, 1f / Sqrt2), - new Vector3(0.0f, -1f, 1f / Sqrt2)), - LinqArray.LinqArray.Create(0, 1, 2, 1, 0, 3, 0, 2, 3, 1, 3, 2)); - - public static readonly IMesh Square - = LinqArray.LinqArray.Create( - new Vector2(-0.5f, -0.5f), - new Vector2(-0.5f, 0.5f), - new Vector2(0.5f, 0.5f), - new Vector2(0.5f, -0.5f)).Select(x => x.ToVector3()).QuadMesh(); - - public static readonly IMesh Octahedron - = Square.Vertices.Append(Vector3.UnitZ, -Vector3.UnitZ).Normalize().TriMesh( - LinqArray.LinqArray.Create( - 0, 1, 4, 1, 2, 4, 2, 3, 4, - 3, 2, 5, 2, 1, 5, 1, 0, 5)); - - // see: https://github.com/mrdoob/three.js/blob/9ef27d1af7809fa4d9943f8d4c4644e365ab6d2d/src/geometries/TorusBufferGeometry.js#L52 - public static Vector3 TorusFunction(Vector2 uv, float radius, float tube) - { - uv *= Math3d.Constants.TwoPi; - return new Vector3( - (radius + tube * uv.Y.Cos()) * uv.X.Cos(), - (radius + tube * uv.Y.Cos()) * uv.X.Sin(), - tube * uv.Y.Sin()); - } - - public static IMesh Torus(float radius, float tubeRadius, int uSegs, int vSegs) - => QuadMesh(uv => TorusFunction(uv, radius, tubeRadius), uSegs, vSegs); - - // see: https://github.com/mrdoob/three.js/blob/9ef27d1af7809fa4d9943f8d4c4644e365ab6d2d/src/geometries/SphereBufferGeometry.js#L76 - public static Vector3 SphereFunction(Vector2 uv, float radius) - => new Vector3( - (float)(-radius * Math.Cos(uv.X * Math3d.Constants.TwoPi) * Math.Sin(uv.Y * Math3d.Constants.Pi)), - (float)(radius * Math.Cos(uv.Y * Math3d.Constants.Pi)), - (float)(radius * Math.Sin(uv.X * Math3d.Constants.TwoPi) * Math.Sin(uv.Y * Math3d.Constants.Pi))); - - public static IMesh Sphere(float radius, int uSegs, int vSegs) - => QuadMesh(uv => SphereFunction(uv, radius), uSegs, vSegs); - - /// - /// Creates a TriMesh from four points. - /// - public static IMesh TriMeshFromQuad(Vector3 a, Vector3 b, Vector3 c, Vector3 d) - => TriMesh(new[] { a, b, c, c, d, a }.ToIArray()); - - // Icosahedron, Dodecahedron, - - /// - /// Returns a collection of circular points. - /// - public static IArray CirclePoints(float radius, int numPoints) - => CirclePoints(numPoints).Select(x => x * radius); - - public static IArray CirclePoints(int numPoints) - => numPoints.Select(i => CirclePoint(i, numPoints)); - - public static Vector2 CirclePoint(int i, int numPoints) - => new Vector2((i * (Math3d.Constants.TwoPi / numPoints)).Cos(), (i * (Math3d.Constants.TwoPi / numPoints)).Sin()); - - /// - /// Computes the indices of a quad mesh astrip. - /// - public static IArray ComputeQuadMeshStripIndices(int usegs, int vsegs, bool wrapUSegs = false, bool wrapVSegs = false) - { - var indices = new List(); - - var maxUSegs = wrapUSegs ? usegs : usegs + 1; - var maxVSegs = wrapVSegs ? vsegs : vsegs + 1; - - for (var i = 0; i < vsegs; ++i) - { - var rowA = i * maxUSegs; - var rowB = ((i + 1) % maxVSegs) * maxUSegs; - - for (var j = 0; j < usegs; ++j) - { - var colA = j; - var colB = (j + 1) % maxUSegs; - - indices.Add(rowA + colA); - indices.Add(rowA + colB); - indices.Add(rowB + colB); - indices.Add(rowB + colA); - } - } - - return indices.ToIArray(); - } - /// /// Returns the index buffer of a quad mesh strip. /// Returns an empty array if either numRowPoints or numPointsPerRow is less than 2. @@ -310,48 +186,5 @@ public static int[] TriMeshCylinderCapIndices(int numEdgeVertices) 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) - => 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) - { - var verts = new List(); - var maxUSegs = wrapUSegs ? usegs : usegs + 1; - var maxVSegs = wrapVSegs ? vsegs : vsegs + 1; - - for (var i = 0; i < maxVSegs; ++i) - { - var v = (float)i / vsegs; - for (var j = 0; j < maxUSegs; ++j) - { - var u = (float)j / usegs; - verts.Add(f(new Vector2(u, v))); - } - } - - 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)); - } } } diff --git a/src/cs/vim/Vim.Format.Core/Geometry/SceneExtensions.cs b/src/cs/vim/Vim.Format.Core/Geometry/SceneExtensions.cs index 8f6e2f9a..f237bcdc 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/SceneExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/SceneExtensions.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using Vim.Util; using Vim.LinqArray; using Vim.Math3d; @@ -14,70 +13,18 @@ public static IMesh TransformedMesh(this ISceneNode node) 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; 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/Validation.cs b/src/cs/vim/Vim.Format.Core/Geometry/Validation.cs deleted file mode 100644 index 5ccca143..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/Validation.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; -using Vim.BFast; -using Vim.G3d; -using Vim.LinqArray; - -namespace Vim.Format.Geometry -{ - public static class Validation - { - public static void ValidateTableRows(this Document doc) - { - foreach (var et in doc.EntityTables.Values.ToArray()) - { - foreach (var c in et.IndexColumns.Values.ToArray()) - { - if (c.Array.Length != et.NumRows) - throw new Exception($"Expected array length {c.Array.Length} of column {c.Name} to be the same as number of rows {et.NumRows}"); - } - - foreach (var c in et.DataColumns.Values.ToArray()) - { - if (c.NumElements() != et.NumRows) - throw new Exception($"Expected array length {c.NumElements()} of column {c.Name} to be the same as number of rows {et.NumRows}"); - } - - foreach (var c in et.StringColumns.Values.ToArray()) - { - if (c.Array.Length != et.NumRows) - throw new Exception($"Expected array length {c.Array.Length} of column {c.Name} to be the same as number of rows {et.NumRows}"); - } - } - } - - public static void ValidateIndexColumns(this Document doc) - { - foreach (var et in doc.EntityTables.Values.ToArray()) - { - foreach (var ic in et.IndexColumns.Values.ToEnumerable()) - { - var table = ic.GetRelatedTable(doc); - if (table == null) - throw new Exception($"Could not find related table for index column {ic.Name}"); - } - } - } - - public static string[] RequiredAttributeNames => new [] - { - // Vertices - CommonAttributes.Position, - CommonAttributes.Index, - - // Meshes - CommonAttributes.MeshSubmeshOffset, - - // Submeshes - CommonAttributes.SubmeshIndexOffset, - - // Instances - CommonAttributes.InstanceMesh, - CommonAttributes.InstanceTransform, - }; - - public static void ValidateGeometryAttributes(this Document doc) - { - var attributes = doc.Geometry.Attributes; - var attributeNameSet = new HashSet(attributes.Select(a => a.Name).ToEnumerable()); - foreach (var attributeName in RequiredAttributeNames) - { - if (!attributeNameSet.Contains(attributeName)) - throw new Exception($"Required attribute {attributeName} was not found."); - } - } - - public static void ValidateAssets(this Document doc) - { - foreach (var asset in doc.Assets.Values.ToEnumerable()) - AssetInfo.Parse(asset.Name); // This will throw if it fails to parse. - } - - public static void Validate(this Document doc) - { - doc.ValidateTableRows(); - doc.ValidateIndexColumns(); - doc.ValidateGeometryAttributes(); - doc.ValidateAssets(); - } - - // TODO: ValidateShapes() to validate VIM files which contain optional 2d data (shapes/overlays). - - public static void ValidateIndices(this IMesh mesh) - { - foreach (var index in mesh.Indices.ToEnumerable()) - { - if (index < 0 || index >= mesh.NumVertices) - throw new Exception($"Invalid mesh index: {index}. Expected a value greater or equal to 0 and less than {mesh.NumVertices}"); - } - } - - public static void Validate(this IMesh mesh) - { - mesh.ValidateIndices(); - } - } -} diff --git a/src/cs/vim/Vim.Format.Core/Vim.Format.Core.csproj b/src/cs/vim/Vim.Format.Core/Vim.Format.Core.csproj index d845180e..77e4bba7 100644 --- a/src/cs/vim/Vim.Format.Core/Vim.Format.Core.csproj +++ b/src/cs/vim/Vim.Format.Core/Vim.Format.Core.csproj @@ -24,19 +24,4 @@ - - - TextTemplatingFileGenerator - ArrayOps.cs - - - - - - True - True - ArrayOps.tt - - - diff --git a/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs b/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs index 9efac292..76d9ed31 100644 --- a/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs +++ b/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs @@ -1,318 +1,318 @@ -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.ToIArray().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()); - } - } -} +//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.ToIArray().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/PerimeterTest.cs b/src/cs/vim/Vim.Format.Tests/Geometry/PerimeterTest.cs deleted file mode 100644 index 82f2ac6c..00000000 --- a/src/cs/vim/Vim.Format.Tests/Geometry/PerimeterTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -using NUnit.Framework; -using Vim.Format.Geometry; -using Vim.Math3d; - -namespace Vim.Format.Tests.Geometry -{ - public static class PerimeterTest - { - - [Test] - public static void Test() - { - var torus = Primitives.QuadMesh(uv => TorusFunction(uv, 10, 0.2f), 10, 24); - - var perimeter = torus.GeneratePerimeter(Vector3.UnitX); - } - - public static Vector3 TorusFunction(Vector2 uv, float radius, float tube) - { - uv = uv * Constants.TwoPi; - return new Vector3( - (radius + tube * uv.Y.Cos()) * uv.X.Cos(), - (radius + tube * uv.Y.Cos()) * uv.X.Sin(), - tube * uv.X.Sin()); - } - } -} diff --git a/src/cs/vim/Vim.Format/Merge/MergeService.cs b/src/cs/vim/Vim.Format/Merge/MergeService.cs index ec44d4f7..a5e0e1f7 100644 --- a/src/cs/vim/Vim.Format/Merge/MergeService.cs +++ b/src/cs/vim/Vim.Format/Merge/MergeService.cs @@ -136,7 +136,7 @@ public static DocumentBuilder MergeVimScenes( ct.ThrowIfCancellationRequested(); var materialCounts = vims.Select(v => v.Materials.Count); - var materialOffsets = materialCounts.ToIArray().PartialSums().DropLast(); + var materialOffsets = materialCounts.ToIArray().PostAccumulate((x, y) => x + y).DropLast(); db.Meshes.AddRange(vims .SelectMany((vim, vimIndex) => vim.Meshes.Select(mesh => (mesh, vimIndex)).ToEnumerable()) @@ -163,7 +163,7 @@ public static DocumentBuilder MergeVimScenes( } var meshCounts = vims.Select(v => v.Meshes.Count); - var meshOffsets = meshCounts.ToIArray().PartialSums().DropLast(); + var meshOffsets = meshCounts.ToIArray().PostAccumulate((x, y) => x + y).DropLast(); // Merge the instances progress?.Report("Merging instances"); diff --git a/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs b/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs index 55dc2b94..78b0ae4f 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs @@ -178,5 +178,19 @@ public static void ValidateEquality(this DocumentBuilder db, VimScene vim) // TODO: compare entity table equality. } } + + private 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}"); + } + } + + private static void Validate(this IMesh mesh) + { + mesh.ValidateIndices(); + } } } diff --git a/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs b/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs index 5364b04c..27d4ecb0 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs @@ -45,7 +45,6 @@ public IMesh GetMesh() // TODO: I think this should be "IEnumerable" in the interface public ISceneNode Parent => null; - public IArray Children => LinqArray.LinqArray.Empty(); public string DisciplineName => VimSceneHelpers.GetDisiplineFromCategory(CategoryName); From 2f3b9a07ac2af53826eef67d2d0e6030664af18a Mon Sep 17 00:00:00 2001 From: Vadym Vorotilin Date: Mon, 2 Dec 2024 11:55:47 +0200 Subject: [PATCH 02/22] Refacroting API: public->private + cleanup unused --- src/cs/vim/Vim.Format.Core/AssetInfo.cs | 39 +++++++------------ src/cs/vim/Vim.Format.Core/BigG3dWriter.cs | 26 ++++++------- .../ColumnExtensions.Buffer.cs | 27 +++++++------ .../ColumnExtensions.Reflection.cs | 9 ++--- .../vim/Vim.Format.Core/ColumnExtensions.cs | 12 +++--- src/cs/vim/Vim.Format.Core/ColumnInfo.cs | 10 ++--- src/cs/vim/Vim.Format.Core/Document.cs | 6 ++- .../DocumentBuilderExtensions.cs | 11 +----- .../Vim.Format.Core/DocumentBuilderTypes.cs | 10 ++--- .../EntityColumnLoaderAttribute.cs | 2 +- src/cs/vim/Vim.Format.Core/EntityTable.cs | 20 +++++----- .../vim/Vim.Format.Core/EntityTableBuilder.cs | 13 ++++--- .../vim/Vim.Format.Core/SerializableHeader.cs | 33 +++++++--------- src/cs/vim/Vim.Format.Core/Serializer.cs | 2 +- .../Vim.Format.Core/TableNameExtensions.cs | 26 +++++++++++++ src/cs/vim/Vim.Format.Core/Validation.cs | 10 ++--- src/cs/vim/Vim.Format.Core/VimConstants.cs | 14 +------ .../vim/Vim.Format.Core/VimFormatVersion.cs | 1 - src/cs/vim/Vim.Format.Core/VimSchema.cs | 2 +- .../vim/Vim.Format/Merge/MergeConfigFiles.cs | 12 ------ src/cs/vim/Vim.Format/Merge/MergeService.cs | 4 +- .../Vim.Format/Merge/MergedTableBuilder.cs | 2 +- .../Merge/RemappedEntityTableBuilder.cs | 4 +- .../vim/Vim.Format/SceneBuilder/VimScene.cs | 2 +- .../SceneBuilder/VimSceneHelpers.cs | 9 ----- 25 files changed, 141 insertions(+), 165 deletions(-) create mode 100644 src/cs/vim/Vim.Format.Core/TableNameExtensions.cs diff --git a/src/cs/vim/Vim.Format.Core/AssetInfo.cs b/src/cs/vim/Vim.Format.Core/AssetInfo.cs index 303c5d27..46a75349 100644 --- a/src/cs/vim/Vim.Format.Core/AssetInfo.cs +++ b/src/cs/vim/Vim.Format.Core/AssetInfo.cs @@ -18,16 +18,16 @@ public enum AssetType /// public class AssetInfo { - public const char Separator = '/'; + private const char Separator = '/'; - public readonly string Name; + public string Name { get; private set; } - public readonly AssetType AssetType; + private readonly AssetType _assetType; public AssetInfo(string name, AssetType assetType) - => (Name, AssetType) = (name, assetType); + => (Name, _assetType) = (name, assetType); - public static string[] SplitAssetBufferName(string assetBufferName) + private static string[] SplitAssetBufferName(string assetBufferName) => assetBufferName?.Split(Separator); public static AssetInfo Parse(string assetBufferName) @@ -63,11 +63,11 @@ public static bool TryParse(string assetBufferName, out AssetInfo assetInfo) } } - public static string AssetTypeToString(AssetType assetType) + private static string AssetTypeToString(AssetType assetType) => assetType.ToString("G").ToLowerInvariant(); - public string AssetTypeString - => AssetTypeToString(AssetType); + private string AssetTypeString + => AssetTypeToString(_assetType); public override string ToString() => $"{AssetTypeString}{Separator}{Name}"; @@ -81,19 +81,13 @@ public string GetDefaultAssetFilePathInDirectory(DirectoryInfo directoryInfo) public static class AssetInfoExtensions { - public static bool IsTexture(this INamedBuffer buffer) - => AssetInfo.TryParse(buffer.Name, out var assetInfo) && assetInfo.AssetType == AssetType.Texture; - - public static INamedBuffer GetAssetBuffer(this Document doc, string assetBufferName) + private static INamedBuffer GetAssetBuffer(this Document doc, string assetBufferName) => doc.Assets.GetOrDefault(assetBufferName); - public static IEnumerable GetTextures(this Document d) - => d.Assets.Values.Where(IsTexture); - /// /// Extracts the asset buffer to the file designated by the given FileInfo. /// - public static FileInfo ExtractAsset(this INamedBuffer assetBuffer, FileInfo fileInfo) + private static FileInfo ExtractAsset(this INamedBuffer assetBuffer, FileInfo fileInfo) { Util.IO.CreateFileDirectory(fileInfo.FullName); using (var stream = fileInfo.Create()) @@ -105,18 +99,11 @@ public static FileInfo ExtractAsset(this INamedBuffer assetBuffer, FileInfo file /// Extracts the asset and returns a FileInfo representing the extracted asset on disk.
/// Returns null if the asset could not be extracted. /// - public static FileInfo ExtractAsset(this INamedBuffer assetBuffer, DirectoryInfo directoryInfo) + private static FileInfo ExtractAsset(this INamedBuffer assetBuffer, DirectoryInfo directoryInfo) => !AssetInfo.TryParse(assetBuffer.Name, out var assetInfo) ? null : assetBuffer.ExtractAsset(new FileInfo(assetInfo.GetDefaultAssetFilePathInDirectory(directoryInfo))); - /// - /// Extracts the asset corresponding to the assetBufferName and returns a FileInfo representing the extracted asset on disk.
- /// Returns null if the asset could not be extracted. - ///
- public static FileInfo ExtractAsset(this Document doc, string assetBufferName, FileInfo fileInfo) - => doc.GetAssetBuffer(assetBufferName)?.ExtractAsset(fileInfo); - /// /// Extracts the assets contained in the Document to the given directory. /// @@ -135,7 +122,7 @@ public static FileInfo ExtractAsset(this Document doc, string assetBufferName, F /// /// Gets the byte array which defines the given asset. Returns false if the asset was not found or if the byte array is empty or null. /// - public static bool TryGetAssetBytes(this Document doc, string assetBufferName, out byte[] bytes) + private static bool TryGetAssetBytes(this Document doc, string assetBufferName, out byte[] bytes) { bytes = null; @@ -151,7 +138,7 @@ public static bool TryGetAssetBytes(this Document doc, string assetBufferName, o /// /// Gets the byte array which defines the given asset. Returns false if the asset was not found or if the byte array is empty or null. /// - public static bool TryGetAssetBytes(this Document doc, AssetType assetType, string assetName, out byte[] bytes) + private static bool TryGetAssetBytes(this Document doc, AssetType assetType, string assetName, out byte[] bytes) => doc.TryGetAssetBytes(new AssetInfo(assetName, assetType).ToString(), out bytes); /// diff --git a/src/cs/vim/Vim.Format.Core/BigG3dWriter.cs b/src/cs/vim/Vim.Format.Core/BigG3dWriter.cs index e70ac297..ccdf6e40 100644 --- a/src/cs/vim/Vim.Format.Core/BigG3dWriter.cs +++ b/src/cs/vim/Vim.Format.Core/BigG3dWriter.cs @@ -13,21 +13,21 @@ namespace Vim.Format /// 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; } + private INamedBuffer Meta { get; } + private string[] Names { get; } + private long[] Sizes { get; } + private BFastHeader Header { get; } + private List Meshes { get; } + private List Instances { get; } + private List Shapes { get; } + private 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; } + private int[] MeshVertexOffsets { get; } + private int[] MeshIndexOffsets { get; } + private int[] MeshSubmeshOffset { get; } + private int[] SubmeshIndexOffsets { get; } + private int[] ShapeVertexOffsets { get; } public BigG3dWriter(List meshes, List instances, List shapes, List materials, G3dHeader? header = null, bool useColors = false) { diff --git a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs index 3cce6b9f..8ae6b0e6 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs @@ -9,8 +9,8 @@ namespace Vim.Format { public static partial class ColumnExtensions { - public static INamedBuffer[] GetAllColumns(this SerializableEntityTable et) - => et.DataColumns.Concat(et.IndexColumns).Concat(et.StringColumns).ToArray(); + private static IEnumerable GetAllColumns(this SerializableEntityTable et) + => et.DataColumns.Concat(et.IndexColumns).Concat(et.StringColumns); public static INamedBuffer[] ValidateColumnRowsAreAligned(this INamedBuffer[] columns) { @@ -32,7 +32,7 @@ public static INamedBuffer[] ValidateColumnRowsAreAligned(this INamedBuffer[] co public static INamedBuffer[] ValidateColumnRowsAreAligned(this SerializableEntityTable et) => et.GetAllColumns().ValidateColumnRowsAreAligned(); - public static string ValidateCanConcatBuffers(this INamedBuffer thisBuffer, INamedBuffer otherBuffer) + private static string ValidateCanConcatBuffers(this INamedBuffer thisBuffer, INamedBuffer otherBuffer) { var thisPrefix = thisBuffer.GetTypePrefix(); if (string.IsNullOrEmpty(thisPrefix)) @@ -54,9 +54,9 @@ public static T[] RemapData(this T[] self, List remapping = null) public static IBuffer ToBuffer(this T[] array) where T : unmanaged => new Buffer(array); - public const string UnknownNamedBufferPrefix = "Unknown NamedBuffer prefix"; + private const string UnknownNamedBufferPrefix = "Unknown NamedBuffer prefix"; - public static object GetDataColumnValue(this IBuffer dataColumn, string typePrefix, int rowIndex) + private static object GetDataColumnValue(this IBuffer dataColumn, string typePrefix, int rowIndex) { switch (typePrefix) { @@ -122,7 +122,7 @@ public static INamedBuffer CopyDataColumn(this INamedBuffer dataColumn, List(this IBuffer thisBuffer, IBuffer otherBuffer) where T : unmanaged + private static IBuffer Concat(this IBuffer thisBuffer, IBuffer otherBuffer) where T : unmanaged => thisBuffer.AsArray().Concat(otherBuffer.AsArray()).ToArray().ToBuffer(); public static IBuffer ConcatDataColumnBuffers(this IBuffer thisBuffer, IBuffer otherBuffer, string typePrefix) @@ -144,14 +144,14 @@ public static IBuffer ConcatDataColumnBuffers(this IBuffer thisBuffer, IBuffer o } } - public static INamedBuffer ConcatDataColumns(this INamedBuffer thisColumn, INamedBuffer otherColumn) + private static INamedBuffer ConcatDataColumns(this INamedBuffer thisColumn, INamedBuffer otherColumn) { var typePrefix = thisColumn.ValidateCanConcatBuffers(otherColumn); var combinedBuffer = thisColumn.ConcatDataColumnBuffers(otherColumn, typePrefix); return new NamedBuffer(combinedBuffer, thisColumn.Name); } - public static List ConcatColumns( + private static List ConcatColumns( this IReadOnlyList thisColumnList, IReadOnlyList otherColumnList, Func concatFunc) where T : INamedBuffer @@ -172,14 +172,15 @@ public static List ConcatColumns( return mergedColumns; } - public static List ConcatDataColumns(this IReadOnlyList thisColumnList, IReadOnlyList otherColumnList) + private static List ConcatDataColumns(this IReadOnlyList thisColumnList, IReadOnlyList otherColumnList) => thisColumnList.ConcatColumns(otherColumnList, (a, b) => a.ConcatDataColumns(b)); - public static List> ConcatIntColumns(this IReadOnlyList> thisColumnList, IReadOnlyList> otherColumnList) + private static List> ConcatIntColumns(this IReadOnlyList> thisColumnList, IReadOnlyList> otherColumnList) => thisColumnList.ConcatColumns(otherColumnList, (a, b) => new NamedBuffer(a.GetTypedData().Concat(b.GetTypedData()).ToArray(), a.Name)); +<<<<<<< HEAD /// /// Returns a concatenated SerializableEntityTable based on the column names of thisTable. /// @@ -200,11 +201,15 @@ public static SerializableEntityTable Concat( public static T[] GetColumnValues(this INamedBuffer nb) where T : unmanaged => nb.AsArray(); +======= + public static IArray GetColumnValues(this INamedBuffer nb) where T : unmanaged + => nb.AsArray().ToIArray(); +>>>>>>> ae310f2 (Refacroting API: public->private + cleanup unused) /// /// Returns a new collection of index columns in which the designated column names have repeated values of VimConstants.NoEntityRelation. /// - public static IEnumerable> NoneIndexColumnRelations( + private static IEnumerable> NoneIndexColumnRelations( this IEnumerable> indexColumns, params string[] indexColumnNames) => indexColumns.Select(ic => indexColumnNames.Contains(ic.Name) diff --git a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs index d7cedece..3fd3a49b 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs @@ -16,10 +16,10 @@ public static string GetDataColumnNameTypePrefix(this Type type) throw new Exception($"{nameof(GetDataColumnNameTypePrefix)} error: no matching data column name prefix for {type}"); } - public static bool CanSerializeAsStringColumn(this Type type) + private static bool CanSerializeAsStringColumn(this Type type) => type == typeof(string); - public static bool CanSerializeAsDataColumn(this Type type) + private static bool CanSerializeAsDataColumn(this Type type) => DataColumnTypes.Contains(type); public static ValueSerializationStrategy GetValueSerializationStrategy(this Type type) @@ -60,10 +60,7 @@ public static string GetSerializedValueColumnName(this FieldInfo fieldInfo) return $"{typePrefix}{fieldInfo.GetSerializedValueName()}"; } - public static string GetSerializedIndexName(this FieldInfo fieldInfo) - => fieldInfo.Name.Trim('_'); - - public static bool IsRelationType(this Type t) + private static bool IsRelationType(this Type t) => t.Name == "Relation`1"; public static Type RelationTypeParameter(this Type t) diff --git a/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs b/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs index 0d826c14..6de4e200 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs @@ -7,7 +7,7 @@ namespace Vim.Format { public static partial class ColumnExtensions { - public static readonly IReadOnlyCollection AllColumnInfos + private static readonly IReadOnlyCollection AllColumnInfos = new[] { new ColumnInfo(ColumnType.IndexColumn, VimConstants.IndexColumnNameTypePrefix, typeof(int)), @@ -22,7 +22,7 @@ public static readonly IReadOnlyCollection AllColumnInfos public static readonly IReadOnlyDictionary TypePrefixToColumnTypeMap = AllColumnInfos.ToDictionary(t => t.TypePrefix, t => t.ColumnType); - public static readonly IReadOnlyDictionary DataColumnTypeToPrefixMap + private static readonly IReadOnlyDictionary DataColumnTypeToPrefixMap = AllColumnInfos .Where(t => t.ColumnType == ColumnType.DataColumn) .SelectMany(t => t.RelatedTypes.Select(type => (Type: type, t.TypePrefix))) @@ -31,13 +31,13 @@ public static readonly IReadOnlyDictionary DataColumnTypeToPrefixM public static readonly ISet DataColumnTypes = new HashSet(AllColumnInfos.Where(t => t.ColumnType == ColumnType.DataColumn).SelectMany(t => t.RelatedTypes)); - public static readonly ISet DataColumnNameTypePrefixes + private static readonly ISet DataColumnNameTypePrefixes = new HashSet(AllColumnInfos.Where(t => t.ColumnType == ColumnType.DataColumn).Select(t => t.TypePrefix)); - public static readonly Regex DataColumnTypePrefixRegex + private static readonly Regex DataColumnTypePrefixRegex = new Regex($@"^(?:{string.Join("|", DataColumnNameTypePrefixes)})"); - public static bool TryGetDataColumnNameTypePrefix(string columnName, out string typePrefix) + private static bool TryGetDataColumnNameTypePrefix(string columnName, out string typePrefix) { typePrefix = null; if (string.IsNullOrEmpty(columnName)) @@ -51,7 +51,7 @@ public static bool TryGetDataColumnNameTypePrefix(string columnName, out string public static bool IsDataColumnName(string columnName) => TryGetDataColumnNameTypePrefix(columnName, out _); - public const string RelatedTableNameFieldNameSeparator = ":"; + private const string RelatedTableNameFieldNameSeparator = ":"; public static string GetIndexColumnName(string relatedTableName, string localFieldName) => VimConstants.IndexColumnNameTypePrefix + relatedTableName + RelatedTableNameFieldNameSeparator + localFieldName; diff --git a/src/cs/vim/Vim.Format.Core/ColumnInfo.cs b/src/cs/vim/Vim.Format.Core/ColumnInfo.cs index dde7b439..29497c36 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnInfo.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnInfo.cs @@ -15,16 +15,16 @@ public class ColumnInfo { public readonly ColumnType ColumnType; public readonly string TypePrefix; - public readonly Type SerializedType; - public readonly ISet CastTypes; + private readonly Type _serializedType; + private readonly ISet _castTypes; public ColumnInfo(ColumnType columnType, string typePrefix, Type serializedType, params Type[] castTypes) { - (ColumnType, TypePrefix, SerializedType) = (columnType, typePrefix, serializedType); - CastTypes = new HashSet(castTypes); + (ColumnType, TypePrefix, _serializedType) = (columnType, typePrefix, serializedType); + _castTypes = new HashSet(castTypes); } public IEnumerable RelatedTypes - => CastTypes.Prepend(SerializedType); + => _castTypes.Prepend(_serializedType); } } diff --git a/src/cs/vim/Vim.Format.Core/Document.cs b/src/cs/vim/Vim.Format.Core/Document.cs index 476bd8e7..39801323 100644 --- a/src/cs/vim/Vim.Format.Core/Document.cs +++ b/src/cs/vim/Vim.Format.Core/Document.cs @@ -14,11 +14,10 @@ public Document(SerializableDocument document) StringTable = _Document.StringTable.ToIArray(); EntityTables = _Document.EntityTables.ToLookup( et => et.Name, - et => et.ToEntityTable(this)); + et => new EntityTable(this, et)); Assets = _Document.Assets.ToLookup(et => et.Name, et => et); } - public string FileName => _Document.FileName; private SerializableDocument _Document { get; } public SerializableHeader Header { get; } public ILookup EntityTables { get; } @@ -26,5 +25,8 @@ public Document(SerializableDocument document) public IArray StringTable { get; } public string GetString(int index) => StringTable.ElementAtOrDefault(index); public G3d.G3D Geometry { get; } + + public EntityTable GetTable(string name) + => EntityTables.GetOrDefault(name); } } diff --git a/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs b/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs index 81e1c2d8..3136b414 100644 --- a/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs @@ -30,10 +30,7 @@ public static SubdividedMesh ToDocumentBuilderSubdividedMesh(this IMesh m) m.SubmeshIndexOffsets.ToList(), m.SubmeshMaterials.ToList()); - public static void AddMesh(this DocumentBuilder db, IMesh m) - => db.AddMesh(m.ToDocumentBuilderSubdividedMesh()); - - public static EntityTableBuilder CreateTableCopy(this DocumentBuilder db, EntityTable table, List nodeIndexRemapping = null) + private static void CreateTableCopy(this DocumentBuilder db, EntityTable table, List nodeIndexRemapping = null) { var name = table.Name; var tb = db.CreateTableBuilder(name); @@ -53,11 +50,9 @@ public static EntityTableBuilder CreateTableCopy(this DocumentBuilder db, Entity var strings = col.GetTypedData().Select(i => table.Document.StringTable.ElementAtOrDefault(i, null)); tb.AddStringColumn(col.Name, strings.ToArray().RemapData(nodeIndexRemapping)); } - - return tb; } - public static DocumentBuilder CopyTablesFrom(this DocumentBuilder db, Document doc, List nodeIndexRemapping = null) + public static void CopyTablesFrom(this DocumentBuilder db, Document doc, List nodeIndexRemapping = null) { foreach (var table in doc.EntityTables.Values.ToEnumerable()) { @@ -69,8 +64,6 @@ public static DocumentBuilder CopyTablesFrom(this DocumentBuilder db, Document d db.CreateTableCopy(table, name == TableNames.Node ? nodeIndexRemapping : null); } - - return db; } public static SerializableEntityTable ToSerializableEntityTable(this EntityTableBuilder tb, diff --git a/src/cs/vim/Vim.Format.Core/DocumentBuilderTypes.cs b/src/cs/vim/Vim.Format.Core/DocumentBuilderTypes.cs index aa57fc46..b401a915 100644 --- a/src/cs/vim/Vim.Format.Core/DocumentBuilderTypes.cs +++ b/src/cs/vim/Vim.Format.Core/DocumentBuilderTypes.cs @@ -36,19 +36,19 @@ public class Material /// public class Mesh { - protected List _vertices = new List(); + private readonly List _vertices; public IReadOnlyList Vertices => _vertices; - protected List _indices = new List(); + private readonly List _indices; public IReadOnlyList Indices => _indices; - protected List _faceMaterials = new List(); + private List _faceMaterials; public IReadOnlyList FaceMaterials => _faceMaterials; - protected List _colors = new List(); + private readonly List _colors; public IReadOnlyList Colors => _colors; - protected List _uvs = new List(); + private readonly List _uvs; public IReadOnlyList UVs => _uvs; public Mesh(List vertices = null, List indices = null, List faceMaterials = null, List colors = null, List uvs = null) diff --git a/src/cs/vim/Vim.Format.Core/EntityColumnLoaderAttribute.cs b/src/cs/vim/Vim.Format.Core/EntityColumnLoaderAttribute.cs index 000c6d12..cb555817 100644 --- a/src/cs/vim/Vim.Format.Core/EntityColumnLoaderAttribute.cs +++ b/src/cs/vim/Vim.Format.Core/EntityColumnLoaderAttribute.cs @@ -5,7 +5,7 @@ namespace Vim.Format { [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] - public partial class EntityColumnLoaderAttribute : Attribute + public class EntityColumnLoaderAttribute : Attribute { /// /// The name of the serialized column. diff --git a/src/cs/vim/Vim.Format.Core/EntityTable.cs b/src/cs/vim/Vim.Format.Core/EntityTable.cs index 44360bf9..f26d9ae4 100644 --- a/src/cs/vim/Vim.Format.Core/EntityTable.cs +++ b/src/cs/vim/Vim.Format.Core/EntityTable.cs @@ -1,6 +1,4 @@ using System; -using System.Diagnostics; -using System.Linq; using Vim.BFast; using Vim.LinqArray; @@ -14,10 +12,9 @@ public EntityTable(Document document, SerializableEntityTable entityTable) _EntityTable = entityTable; Name = _EntityTable.Name; - DataColumns = LinqArray.LinqArray.ToLookup(_EntityTable.DataColumns, c => c.Name, c => c); - IndexColumns = LinqArray.LinqArray.ToLookup(_EntityTable.IndexColumns, c => c.Name, c => c); - StringColumns = LinqArray.LinqArray.ToLookup(_EntityTable.StringColumns, c => c.Name, c => c); - Columns = entityTable.ValidateColumnRowsAreAligned(); + DataColumns = _EntityTable.DataColumns.ToLookup(c => c.Name, c => c); + IndexColumns = _EntityTable.IndexColumns.ToLookup(c => c.Name, c => c); + StringColumns = _EntityTable.StringColumns.ToLookup(c => c.Name, c => c); NumRows = Columns.FirstOrDefault()?.NumElements() ?? 0; } @@ -25,10 +22,13 @@ public EntityTable(Document document, SerializableEntityTable entityTable) public Document Document { get; } public string Name { get; } public int NumRows { get; } - public LinqArray.ILookup DataColumns { get; } - public LinqArray.ILookup> StringColumns { get; } - public LinqArray.ILookup> IndexColumns { get; } - public INamedBuffer[] Columns { get; } + public ILookup DataColumns { get; } + public ILookup> StringColumns { get; } + public ILookup> IndexColumns { get; } + public IArray Columns + => DataColumns.Values + .Concatenate(IndexColumns.Values.Select(x => (INamedBuffer)x)) + .Concatenate(StringColumns.Values.Select(x => (INamedBuffer)x)); public IArray GetIndexColumnValues(string columnName) => IndexColumns.GetOrDefault(columnName)?.GetColumnValues().ToIArray(); diff --git a/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs b/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs index bd1da0fd..b985100b 100644 --- a/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs +++ b/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs @@ -17,20 +17,19 @@ public class EntityTableBuilder public EntityTableBuilder(string name) => Name = name; - public EntityTableBuilder UpdateOrValidateRows(int n) + private void UpdateOrValidateRows(int n) { if (NumRows == 0) NumRows = n; else if (NumRows != n) throw new Exception($"Value count {n} does not match the expected number of rows {NumRows}"); - return this; } - public void ValidateHasDataColumnPrefix(string columnName) + private static void ValidateHasDataColumnPrefix(string columnName) { if (!ColumnExtensions.IsDataColumnName(columnName)) throw new Exception($"{nameof(columnName)} {columnName} does not begin with a data column prefix"); } - public void ValidateHasPrefix(string columnName, string expectedPrefix) + private static void ValidateHasPrefix(string columnName, string expectedPrefix) { if (!columnName.StartsWith(expectedPrefix)) throw new Exception($"{nameof(columnName)} {columnName} must start with {expectedPrefix}"); @@ -44,8 +43,10 @@ public EntityTableBuilder AddIndexColumn(string columnName, int[] indices) return this; } - public EntityTableBuilder AddIndexColumn(string columnName, IEnumerable ids) - => AddIndexColumn(columnName, ids.ToArray()); + public void AddIndexColumn(string columnName, IEnumerable ids) + { + AddIndexColumn(columnName, ids.ToArray()); + } public EntityTableBuilder AddStringColumn(string columnName, string[] values) { diff --git a/src/cs/vim/Vim.Format.Core/SerializableHeader.cs b/src/cs/vim/Vim.Format.Core/SerializableHeader.cs index c1c3a97c..59b06bc2 100644 --- a/src/cs/vim/Vim.Format.Core/SerializableHeader.cs +++ b/src/cs/vim/Vim.Format.Core/SerializableHeader.cs @@ -8,23 +8,23 @@ namespace Vim.Format { public class SerializableHeader { - public static readonly SerializableVersion CurrentVimFormatVersion = VimFormatVersion.Current; - - public const string FormatVersionField = "vim"; - public const string IdField = "id"; - public const string RevisionField = "revision"; - public const string GeneratorField = "generator"; - public const string CreationDateField = "created"; - public const string SchemaField = "schema"; + private static readonly SerializableVersion CurrentVimFormatVersion = VimFormatVersion.Current; + + private const string FormatVersionField = "vim"; + private const string IdField = "id"; + private const string RevisionField = "revision"; + private const string GeneratorField = "generator"; + private const string CreationDateField = "created"; + private const string SchemaField = "schema"; public const string BuildField = "build"; // optional - public const char Separator = '='; - public const char EndOfLineChar = '\n'; - public const string EndOfLineString = "\n"; - public const string PersistingIdSeparator = "::"; - public const string DummyPersistingIdPrefix = "unknown_"; + private const char Separator = '='; + private const char EndOfLineChar = '\n'; + private const string EndOfLineString = "\n"; + private const string PersistingIdSeparator = "::"; + private const string DummyPersistingIdPrefix = "unknown_"; - public static readonly string[] RequiredFields = + private static readonly string[] RequiredFields = { FormatVersionField, IdField, @@ -252,9 +252,6 @@ public override bool Equals(object obj) public override int GetHashCode() => ToString().GetHashCode(); - public static string CreatePersistingId(Guid id, Guid revision) - => string.Join(PersistingIdSeparator, id.ToString(), revision.ToString()); - /// /// Used to generate an unknown persistence ID to avoid id collisions with other unknown references. /// @@ -272,6 +269,6 @@ public static string CreateDummyPersistingId() /// /// public string PersistingId - => CreatePersistingId(Id, Revision); + => string.Join(PersistingIdSeparator, Id.ToString(), Revision.ToString()); } } diff --git a/src/cs/vim/Vim.Format.Core/Serializer.cs b/src/cs/vim/Vim.Format.Core/Serializer.cs index 06dce92e..402d124d 100644 --- a/src/cs/vim/Vim.Format.Core/Serializer.cs +++ b/src/cs/vim/Vim.Format.Core/Serializer.cs @@ -320,7 +320,7 @@ public static SerializableDocument Deserialize(string filePath, LoadOptions load using (var stream = File.OpenRead(filePath)) { var doc = Deserialize(stream, loadOptions); - doc.SetFileName(filePath); + doc.FileName = filePath; return doc; } } diff --git a/src/cs/vim/Vim.Format.Core/TableNameExtensions.cs b/src/cs/vim/Vim.Format.Core/TableNameExtensions.cs new file mode 100644 index 00000000..396cca53 --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/TableNameExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Text.RegularExpressions; +using Vim.BFast; + +namespace Vim.Format +{ + public static class TableNameExtensions + { + private static readonly Regex IndexColumnNameComponentsRegex = new Regex(@"(\w+:)((?:\w|\.)+):(.+)"); + + public static string GetRelatedTableNameFromIndexColumnName(string indexColumnName) + { + var match = IndexColumnNameComponentsRegex.Match(indexColumnName); + if (!match.Success) + throw new Exception($"Index column name {indexColumnName} could not be separated into its components."); + + return match.Groups[2].Value; + } + + public static string GetRelatedTableName(this INamedBuffer ic) + => GetRelatedTableNameFromIndexColumnName(ic.Name); + + public static EntityTable GetRelatedTable(this INamedBuffer ic, Document doc) + => doc.GetTable(ic.GetRelatedTableName()); + } +} diff --git a/src/cs/vim/Vim.Format.Core/Validation.cs b/src/cs/vim/Vim.Format.Core/Validation.cs index bb5287ab..31026d99 100644 --- a/src/cs/vim/Vim.Format.Core/Validation.cs +++ b/src/cs/vim/Vim.Format.Core/Validation.cs @@ -8,7 +8,7 @@ namespace Vim.Format { public static class Validation { - public static void ValidateTableRows(this Document doc) + private static void ValidateTableRows(this Document doc) { foreach (var et in doc.EntityTables.Values.ToArray()) { @@ -32,7 +32,7 @@ public static void ValidateTableRows(this Document doc) } } - public static void ValidateIndexColumns(this Document doc) + private static void ValidateIndexColumns(this Document doc) { foreach (var et in doc.EntityTables.Values.ToArray()) { @@ -45,7 +45,7 @@ public static void ValidateIndexColumns(this Document doc) } } - public static string[] RequiredAttributeNames => new [] + private static string[] RequiredAttributeNames => new [] { // Vertices CommonAttributes.Position, @@ -62,7 +62,7 @@ public static void ValidateIndexColumns(this Document doc) CommonAttributes.InstanceTransform, }; - public static void ValidateGeometryAttributes(this Document doc) + private static void ValidateGeometryAttributes(this Document doc) { var attributes = doc.Geometry.Attributes; var attributeNameSet = new HashSet(attributes.Select(a => a.Name).ToEnumerable()); @@ -73,7 +73,7 @@ public static void ValidateGeometryAttributes(this Document doc) } } - public static void ValidateAssets(this Document doc) + private static void ValidateAssets(this Document doc) { foreach (var asset in doc.Assets.Values.ToEnumerable()) AssetInfo.Parse(asset.Name); // This will throw if it fails to parse. diff --git a/src/cs/vim/Vim.Format.Core/VimConstants.cs b/src/cs/vim/Vim.Format.Core/VimConstants.cs index 5e061573..09519a5c 100644 --- a/src/cs/vim/Vim.Format.Core/VimConstants.cs +++ b/src/cs/vim/Vim.Format.Core/VimConstants.cs @@ -95,26 +95,16 @@ public static class VimConstants public const string BuildingParameterHolderElementType = "Building Parameter Holder"; public const string SiteParameterHolderElementType = "Site Parameter Holder"; - public static HashSet ComputedTableNames = new HashSet + public static readonly HashSet ComputedTableNames = new HashSet { TableNames.Geometry }; - public static HashSet NonBimNames = new HashSet + public static readonly HashSet NonBimNames = new HashSet { TableNames.Geometry, TableNames.Asset, TableNames.Material, }; - - public static class DisciplineNames - { - public const string Mechanical = nameof(Mechanical); - public const string Architecture = nameof(Architecture); - public const string Generic = nameof(Generic); - public const string Electrical = nameof(Electrical); - public const string Plumbing = nameof(Plumbing); - public const string Structural = nameof(Structural); - } } } diff --git a/src/cs/vim/Vim.Format.Core/VimFormatVersion.cs b/src/cs/vim/Vim.Format.Core/VimFormatVersion.cs index 2751a909..b0cecbb2 100644 --- a/src/cs/vim/Vim.Format.Core/VimFormatVersion.cs +++ b/src/cs/vim/Vim.Format.Core/VimFormatVersion.cs @@ -6,6 +6,5 @@ public static class VimFormatVersion { public static SerializableVersion Current => v1_0_0; public static SerializableVersion v1_0_0 => SerializableVersion.Parse("1.0.0"); - public static SerializableVersion v0_9_0 => SerializableVersion.Parse("0.9.0"); } } diff --git a/src/cs/vim/Vim.Format.Core/VimSchema.cs b/src/cs/vim/Vim.Format.Core/VimSchema.cs index a8a946e0..f23d783a 100644 --- a/src/cs/vim/Vim.Format.Core/VimSchema.cs +++ b/src/cs/vim/Vim.Format.Core/VimSchema.cs @@ -45,7 +45,7 @@ public EntityTableSchema AddEntityTableSchema(string entityTableName) } public static VimSchema Create(string filePath) - => Create(Serializer.Deserialize(filePath).ToDocument()); + => Create(new Document(Serializer.Deserialize(filePath))); public static VimSchema Create(Document doc) { diff --git a/src/cs/vim/Vim.Format/Merge/MergeConfigFiles.cs b/src/cs/vim/Vim.Format/Merge/MergeConfigFiles.cs index 5ec67293..80cc7c9a 100644 --- a/src/cs/vim/Vim.Format/Merge/MergeConfigFiles.cs +++ b/src/cs/vim/Vim.Format/Merge/MergeConfigFiles.cs @@ -13,18 +13,6 @@ public class MergeConfigFiles /// public (string VimFilePath, Matrix4x4 Transform)[] InputVimFilePathsAndTransforms { get; } - /// - /// The input VIM file paths - /// - public string[] InputVimFilePaths - => InputVimFilePathsAndTransforms.Select(t => t.VimFilePath).ToArray(); - - /// - /// The input VIM file path transforms - /// - public Matrix4x4[] InputTransforms - => InputVimFilePathsAndTransforms.Select(t => t.Transform).ToArray(); - /// /// The merged VIM file path. /// diff --git a/src/cs/vim/Vim.Format/Merge/MergeService.cs b/src/cs/vim/Vim.Format/Merge/MergeService.cs index a5e0e1f7..eb6ac945 100644 --- a/src/cs/vim/Vim.Format/Merge/MergeService.cs +++ b/src/cs/vim/Vim.Format/Merge/MergeService.cs @@ -195,7 +195,7 @@ public static DocumentBuilder MergeVimScenes( /// /// Throws if the given VIM files do not all have the same object model schema major version. /// - public static void ValidateSameObjectModelSchemaMajorVersion(VimScene[] vims) + private static void ValidateSameObjectModelSchemaMajorVersion(VimScene[] vims) { var objectModelMajorVersions = vims .Select(v => v.Document.Header.Schema.Major) @@ -222,7 +222,7 @@ public static void ValidateSameObjectModelSchemaMajorVersion(VimScene[] vims) /// /// Returns a collection of transforms based on the largest dimension of the largest VIM bounding box. /// - public static Matrix4x4[] GetGridTransforms(VimScene[] vims, float padding) + private static Matrix4x4[] GetGridTransforms(VimScene[] vims, float padding) { var boxes = vims.Select(v => v.BoundingBox()).ToArray(); var centerBottomTransforms = boxes.Select(b => Matrix4x4.CreateTranslation(-b.CenterBottom)).ToIArray(); diff --git a/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs b/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs index 3237114e..a67fad64 100644 --- a/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs +++ b/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs @@ -8,7 +8,7 @@ namespace Vim.Format.Merge { - public class MergedTableBuilder + internal class MergedTableBuilder { public readonly string Name; public int NumRows; diff --git a/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs b/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs index 231bb52a..6f37e171 100644 --- a/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs +++ b/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs @@ -6,7 +6,7 @@ namespace Vim.Format.Merge { - public class RemappedEntityTableBuilder + internal class RemappedEntityTableBuilder { public EntityTableBuilder EntityTableBuilder { get; } public int[] OldToNewIndexMap { get; } @@ -137,7 +137,7 @@ private static void UpdateEntityTableBuilderRelations( var indexColumn = kv.Value; // Get the related index remapping. - var tableName = DocumentExtensions.GetRelatedTableNameFromColumnName(indexColumnName); + var tableName = TableNameExtensions.GetTableNameFromIndexColumnName(indexColumnName); if (!remappedTableIndices.TryGetValue(tableName, out var oldToNewIndexMap)) continue; diff --git a/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs b/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs index 0d744a39..424cda93 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs @@ -100,7 +100,7 @@ private Action[] GetInitStepsWithProgress(bool inParallel, IVimSceneProgress pro private IStep[] GetInitSteps(bool inParallel) { var createDocument = new Step( - () => Document = _SerializableDocument.ToDocument(), + () => Document = new Document(_SerializableDocument), "Creating Document" ); diff --git a/src/cs/vim/Vim.Format/SceneBuilder/VimSceneHelpers.cs b/src/cs/vim/Vim.Format/SceneBuilder/VimSceneHelpers.cs index 3c6e5ceb..d98c49bb 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/VimSceneHelpers.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/VimSceneHelpers.cs @@ -163,16 +163,7 @@ public static Vector4 ToDiffuseColor(this DVector3 v, double transparency) public static IEnumerable<(string assetBufferName, FileInfo assetFileInfo)> ExtractAssets(this VimScene vim, DirectoryInfo directory) => vim.Document.ExtractAssets(directory); - public static FileInfo ExtractAsset(this VimScene vim, string assetBufferName, FileInfo fileInfo) - => vim.Document.ExtractAsset(assetBufferName, fileInfo); - - public static ElementInfo GetElementInfo(this VimScene vim, int elementIndex) - => vim.DocumentModel.GetElementInfo(elementIndex); - public static ElementInfo GetElementInfo(this VimScene vim, Element element) => vim.DocumentModel.GetElementInfo(element); - - public static VimSchema GetVimSchema(this VimScene vim) - => VimSchema.Create(vim.Document); } } From 27eedd7475d02e7c938b3829d00defdee38a2267 Mon Sep 17 00:00:00 2001 From: Vadym Vorotilin Date: Mon, 2 Dec 2024 11:56:14 +0200 Subject: [PATCH 03/22] Partially uncommented geometry tests --- .../Geometry/GeometryTests.cs | 636 +++++++++--------- 1 file changed, 318 insertions(+), 318 deletions(-) diff --git a/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs b/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs index 76d9ed31..738effdd 100644 --- a/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs +++ b/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs @@ -1,318 +1,318 @@ -//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.ToIArray().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()); -// } -// } -//} +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.ToIArray().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()); + } + } +} From cbb32328bc1be610a24659f82447520b9ce47bfe Mon Sep 17 00:00:00 2001 From: Vadym Vorotilin Date: Mon, 2 Dec 2024 12:34:23 +0200 Subject: [PATCH 04/22] Name fix --- src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs b/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs index 6f37e171..7dc6b65f 100644 --- a/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs +++ b/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs @@ -137,7 +137,7 @@ private static void UpdateEntityTableBuilderRelations( var indexColumn = kv.Value; // Get the related index remapping. - var tableName = TableNameExtensions.GetTableNameFromIndexColumnName(indexColumnName); + var tableName = TableNameExtensions.GetRelatedTableNameFromIndexColumnName(indexColumnName); if (!remappedTableIndices.TryGetValue(tableName, out var oldToNewIndexMap)) continue; From dff907fbb5b7f3ac192e156cbbbbe72036c21ede Mon Sep 17 00:00:00 2001 From: Vadym Vorotilin Date: Tue, 3 Dec 2024 15:58:14 +0200 Subject: [PATCH 05/22] Copied refactored code from branch sroberge/vimx_stable --- .../bfast/Vim.BFast.Tests/BFastTestProgram.cs | 198 ------ src/cs/bfast/Vim.BFast.Tests/BFastTests.cs | 576 ++++++++++++++++++ .../Vim.BFast.Tests/Vim.BFast.Tests.csproj | 35 +- src/cs/bfast/Vim.BFast/BFast.cs | 439 ------------- src/cs/bfast/Vim.BFast/BFast/BFast.cs | 197 ++++++ .../bfast/Vim.BFast/BFast/BFastArrayNode.cs | 47 ++ .../Vim.BFast/BFast/BFastEnumerableNode.cs | 59 ++ src/cs/bfast/Vim.BFast/BFast/BFastHelpers.cs | 46 ++ .../bfast/Vim.BFast/BFast/BFastStreamNode.cs | 50 ++ .../bfast/Vim.BFast/BFast/CompressibleNode.cs | 83 +++ src/cs/bfast/Vim.BFast/BFast/IBFastNode.cs | 31 + src/cs/bfast/Vim.BFast/BFastBufferReader.cs | 177 ------ src/cs/bfast/Vim.BFast/BFastBuilder.cs | 93 --- src/cs/bfast/Vim.BFast/BFastStructs.cs | 113 ---- .../{ => Buffers}/BufferExtensions.cs | 5 +- .../bfast/Vim.BFast/{ => Buffers}/Buffers.cs | 3 +- src/cs/bfast/Vim.BFast/Core/BFastConstants.cs | 15 + src/cs/bfast/Vim.BFast/Core/BFastHeader.cs | 85 +++ src/cs/bfast/Vim.BFast/Core/BFastPreamble.cs | 68 +++ src/cs/bfast/Vim.BFast/Core/BFastRange.cs | 44 ++ src/cs/bfast/Vim.BFast/Core/BFastSection.cs | 103 ++++ src/cs/bfast/Vim.BFast/Core/BFastStrings.cs | 46 ++ src/cs/bfast/Vim.BFast/Core/BFastWriter.cs | 100 +++ src/cs/bfast/Vim.BFast/SeekContext.cs | 41 -- .../Vim.BFast/Unsafe/MemStreamHelpers.cs | 38 ++ src/cs/bfast/Vim.BFast/Unsafe/UnsafeCast.cs | 66 ++ .../bfast/Vim.BFast/Unsafe/UnsafeHelpers.cs | 49 ++ .../bfast/Vim.BFast/Unsafe/UnsafeReadArray.cs | 93 +++ .../Vim.BFast/Unsafe/UnsafeReadEnumerable.cs | 70 +++ src/cs/bfast/Vim.BFast/Unsafe/UnsafeWrite.cs | 72 +++ src/cs/bfast/Vim.BFast/UnsafeHelpers.cs | 116 ---- src/cs/bfast/Vim.BFast/Vim.BFast.csproj | 25 +- src/cs/bfast/Vim.BFast/readme.md | 76 --- src/cs/bfast/Vim.BFast/spec.txt | 100 --- .../Vim.G3d.AssimpWrapper.csproj | 7 +- src/cs/g3d/Vim.G3d.Tests/G3dTestUtils.cs | 137 ----- src/cs/g3d/Vim.G3d.Tests/G3dTests.cs | 130 ++-- .../Properties/Resources.Designer.cs | 4 +- src/cs/g3d/Vim.G3d.Tests/Vim.G3d.Tests.csproj | 8 + src/cs/g3d/Vim.G3d/G3D.cs | 45 +- src/cs/g3d/Vim.G3d/G3dSerialization.cs | 108 +--- src/cs/g3d/Vim.G3d/G3dWriter.cs | 47 -- src/cs/g3d/Vim.G3d/GeometryAttribute.cs | 21 +- src/cs/g3d/Vim.G3d/Header.cs | 12 + src/cs/g3d/Vim.G3d/Vim.G3d.csproj | 9 +- src/cs/g3d/Vim.G3dNext.CodeGen/CodeBuilder.cs | 73 +++ src/cs/g3d/Vim.G3dNext.CodeGen/Definitions.cs | 62 ++ src/cs/g3d/Vim.G3dNext.CodeGen/G3dBuffer.cs | 48 ++ src/cs/g3d/Vim.G3dNext.CodeGen/G3dCodeGen.cs | 134 ++++ src/cs/g3d/Vim.G3dNext.CodeGen/G3dEntity.cs | 34 ++ src/cs/g3d/Vim.G3dNext.CodeGen/Program.cs | 11 + .../Vim.G3dNext.CodeGen.csproj | 26 + .../Vim.G3dNext.Tests.csproj | 37 ++ src/cs/g3d/Vim.G3dNext/BufferMethods.cs | 63 ++ src/cs/g3d/Vim.G3dNext/Constants.cs | 47 ++ src/cs/g3d/Vim.G3dNext/G3dChunk.cs | 164 +++++ src/cs/g3d/Vim.G3dNext/G3dGenerated.g.cs | 471 ++++++++++++++ src/cs/g3d/Vim.G3dNext/G3dMaterials.cs | 18 + src/cs/g3d/Vim.G3dNext/G3dScene.cs | 12 + src/cs/g3d/Vim.G3dNext/G3dVim.cs | 185 ++++++ src/cs/g3d/Vim.G3dNext/MetaHeader.cs | 63 ++ src/cs/g3d/Vim.G3dNext/Vim.G3dNext.csproj | 22 + src/cs/vim/Vim.Format.Core/AssetInfo.cs | 2 +- src/cs/vim/Vim.Format.Core/BigG3dWriter.cs | 420 ++++++------- .../ColumnExtensions.Buffer.cs | 2 +- src/cs/vim/Vim.Format.Core/Document.cs | 2 +- src/cs/vim/Vim.Format.Core/DocumentBuilder.cs | 117 ++-- .../DocumentBuilderExtensions.cs | 2 +- src/cs/vim/Vim.Format.Core/EntityTable.cs | 2 +- .../vim/Vim.Format.Core/EntityTableBuilder.cs | 2 +- src/cs/vim/Vim.Format.Core/G3dBuilder.cs | 133 ++++ .../Vim.Format.Core/Geometry/Validation.cs | 106 ++++ .../Vim.Format.Core/SerializableDocument.cs | 193 ++++-- .../SerializableEntityTable.cs | 69 +++ .../vim/Vim.Format.Core/SerializableHeader.cs | 69 ++- src/cs/vim/Vim.Format.Core/Serializer.cs | 576 ++++++++---------- .../Vim.Format.Core/TableNameExtensions.cs | 2 +- .../Vim.Format.Core/TypePrefixExtensions.cs | 22 + src/cs/vim/Vim.Format.Core/Validation.cs | 2 +- src/cs/vim/Vim.Format.Core/VimSchema.cs | 2 +- src/cs/vim/Vim.Format.Tests/FormatTests.cs | 2 +- .../Geometry/GeometryTests.cs | 4 +- src/cs/vim/Vim.Format/Merge/MergeService.cs | 8 +- .../Vim.Format/Merge/MergedTableBuilder.cs | 8 +- .../vim/Vim.Format/SceneBuilder/Validation.cs | 32 +- .../vim/Vim.Format/SceneBuilder/VimScene.cs | 53 +- 86 files changed, 4721 insertions(+), 2466 deletions(-) delete mode 100644 src/cs/bfast/Vim.BFast.Tests/BFastTestProgram.cs create mode 100644 src/cs/bfast/Vim.BFast.Tests/BFastTests.cs delete mode 100644 src/cs/bfast/Vim.BFast/BFast.cs create mode 100644 src/cs/bfast/Vim.BFast/BFast/BFast.cs create mode 100644 src/cs/bfast/Vim.BFast/BFast/BFastArrayNode.cs create mode 100644 src/cs/bfast/Vim.BFast/BFast/BFastEnumerableNode.cs create mode 100644 src/cs/bfast/Vim.BFast/BFast/BFastHelpers.cs create mode 100644 src/cs/bfast/Vim.BFast/BFast/BFastStreamNode.cs create mode 100644 src/cs/bfast/Vim.BFast/BFast/CompressibleNode.cs create mode 100644 src/cs/bfast/Vim.BFast/BFast/IBFastNode.cs delete mode 100644 src/cs/bfast/Vim.BFast/BFastBufferReader.cs delete mode 100644 src/cs/bfast/Vim.BFast/BFastBuilder.cs delete mode 100644 src/cs/bfast/Vim.BFast/BFastStructs.cs rename src/cs/bfast/Vim.BFast/{ => Buffers}/BufferExtensions.cs (97%) rename src/cs/bfast/Vim.BFast/{ => Buffers}/Buffers.cs (97%) create mode 100644 src/cs/bfast/Vim.BFast/Core/BFastConstants.cs create mode 100644 src/cs/bfast/Vim.BFast/Core/BFastHeader.cs create mode 100644 src/cs/bfast/Vim.BFast/Core/BFastPreamble.cs create mode 100644 src/cs/bfast/Vim.BFast/Core/BFastRange.cs create mode 100644 src/cs/bfast/Vim.BFast/Core/BFastSection.cs create mode 100644 src/cs/bfast/Vim.BFast/Core/BFastStrings.cs create mode 100644 src/cs/bfast/Vim.BFast/Core/BFastWriter.cs delete mode 100644 src/cs/bfast/Vim.BFast/SeekContext.cs create mode 100644 src/cs/bfast/Vim.BFast/Unsafe/MemStreamHelpers.cs create mode 100644 src/cs/bfast/Vim.BFast/Unsafe/UnsafeCast.cs create mode 100644 src/cs/bfast/Vim.BFast/Unsafe/UnsafeHelpers.cs create mode 100644 src/cs/bfast/Vim.BFast/Unsafe/UnsafeReadArray.cs create mode 100644 src/cs/bfast/Vim.BFast/Unsafe/UnsafeReadEnumerable.cs create mode 100644 src/cs/bfast/Vim.BFast/Unsafe/UnsafeWrite.cs delete mode 100644 src/cs/bfast/Vim.BFast/UnsafeHelpers.cs delete mode 100644 src/cs/bfast/Vim.BFast/readme.md delete mode 100644 src/cs/bfast/Vim.BFast/spec.txt delete mode 100644 src/cs/g3d/Vim.G3d.Tests/G3dTestUtils.cs delete mode 100644 src/cs/g3d/Vim.G3d/G3dWriter.cs create mode 100644 src/cs/g3d/Vim.G3dNext.CodeGen/CodeBuilder.cs create mode 100644 src/cs/g3d/Vim.G3dNext.CodeGen/Definitions.cs create mode 100644 src/cs/g3d/Vim.G3dNext.CodeGen/G3dBuffer.cs create mode 100644 src/cs/g3d/Vim.G3dNext.CodeGen/G3dCodeGen.cs create mode 100644 src/cs/g3d/Vim.G3dNext.CodeGen/G3dEntity.cs create mode 100644 src/cs/g3d/Vim.G3dNext.CodeGen/Program.cs create mode 100644 src/cs/g3d/Vim.G3dNext.CodeGen/Vim.G3dNext.CodeGen.csproj create mode 100644 src/cs/g3d/Vim.G3dNext.Tests/Vim.G3dNext.Tests.csproj create mode 100644 src/cs/g3d/Vim.G3dNext/BufferMethods.cs create mode 100644 src/cs/g3d/Vim.G3dNext/Constants.cs create mode 100644 src/cs/g3d/Vim.G3dNext/G3dChunk.cs create mode 100644 src/cs/g3d/Vim.G3dNext/G3dGenerated.g.cs create mode 100644 src/cs/g3d/Vim.G3dNext/G3dMaterials.cs create mode 100644 src/cs/g3d/Vim.G3dNext/G3dScene.cs create mode 100644 src/cs/g3d/Vim.G3dNext/G3dVim.cs create mode 100644 src/cs/g3d/Vim.G3dNext/MetaHeader.cs create mode 100644 src/cs/g3d/Vim.G3dNext/Vim.G3dNext.csproj create mode 100644 src/cs/vim/Vim.Format.Core/G3dBuilder.cs create mode 100644 src/cs/vim/Vim.Format.Core/Geometry/Validation.cs create mode 100644 src/cs/vim/Vim.Format.Core/SerializableEntityTable.cs create mode 100644 src/cs/vim/Vim.Format.Core/TypePrefixExtensions.cs diff --git a/src/cs/bfast/Vim.BFast.Tests/BFastTestProgram.cs b/src/cs/bfast/Vim.BFast.Tests/BFastTestProgram.cs deleted file mode 100644 index f4b3cab8..00000000 --- a/src/cs/bfast/Vim.BFast.Tests/BFastTestProgram.cs +++ /dev/null @@ -1,198 +0,0 @@ -/* - BFAST - Binary Format for Array Streaming and Transmission - Copyright 2019, VIMaec LLC - Copyright 2018, Ara 3D, Inc. - Usage licensed under terms of MIT License - https://github.com/vimaec/bfast -*/ - -using NUnit.Framework; -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Numerics; - -namespace Vim.BFast.Tests -{ - public static class BFastTests - { - public static int Mb = 1000 * 1000; - public static int Gb = 1000 * Mb; - - static byte[] ByteArray(int numBytes) => - Enumerable.Range(0, numBytes).Select(i => (byte)i).ToArray(); - - static readonly byte[] Array1MB - = ByteArray(Mb); - - static readonly double[] Array1GB - = Enumerable.Range(0, Gb / 8).Select(i => (double)i).ToArray(); - - public static (string, byte[])[] ZeroBuffers - = Enumerable.Empty<(string, byte[])>().ToArray(); - - public static (string, byte[])[] Ten1MBBuffers - = Enumerable.Range(0, 10).Select(i => (i.ToString(), Array1MB)).ToArray(); - - public static (string, double[])[] One1GBBuffer - => Enumerable.Range(0, 1).Select(i => (i.ToString(), Array1GB)).ToArray(); - - public static void TestBFastBytes(byte[] bytes) - { - Console.WriteLine($"Size of buffer = {bytes.Length}"); - Console.WriteLine($"First 8 bytes = {string.Join(", ", bytes.Take(8))}"); - } - - public class DisposableTimer : IDisposable - { - Stopwatch Stopwatch = Stopwatch.StartNew(); - - public void Dispose() - => Console.WriteLine($"Elapsed = {Stopwatch.ElapsedMilliseconds / 1000}s {Stopwatch.ElapsedMilliseconds % 1000}ms"); - } - - public static DisposableTimer CreateTimer(string message = null) - { - Console.WriteLine($"Starting timer {message ?? string.Empty}"); - return new DisposableTimer(); - } - - [Test] - public static void TestStringPacking() - { - var noStrings = new string[0]; - var oneStrings = new[] { "" }; - var twoStrings = new[] { "", "ab" }; - var threeStrings = new[] { "a", "b", "" }; - var noPacked = BFast.PackStrings(noStrings); - var onePacked = BFast.PackStrings(oneStrings); - var twoPacked = BFast.PackStrings(twoStrings); - var threePacked = BFast.PackStrings(threeStrings); - Assert.AreEqual(0, noPacked.Length); - Assert.AreEqual(1, onePacked.Length); - Assert.AreEqual(4, twoPacked.Length); - Assert.AreEqual(5, threePacked.Length); - Assert.AreEqual(noStrings, BFast.UnpackStrings(noPacked)); - Assert.AreEqual(oneStrings, BFast.UnpackStrings(onePacked)); - Assert.AreEqual(twoStrings, BFast.UnpackStrings(twoPacked)); - Assert.AreEqual(threeStrings, BFast.UnpackStrings(threePacked)); - } - - [Test] - public static void BasicTests() - { - using (CreateTimer("ZeroBuffers")) - { - var bytes = BFast.WriteBFastToBytes(ZeroBuffers); - TestBFastBytes(bytes); - var tmp = BFast.ReadBFast(bytes).ToArray(); - Assert.AreEqual(0, tmp.Length); - } - using (CreateTimer("Ten1MBBuffers")) - { - var bytes = BFast.WriteBFastToBytes(Ten1MBBuffers); - TestBFastBytes(bytes); - var tmp = BFast.ReadBFast(bytes).ToArray(); - Assert.AreEqual(10, tmp.Length); - Assert.AreEqual(tmp.Select(x => x.Name).ToArray(), Enumerable.Range(0, 10).Select(x => x.ToString()).ToArray()); - Assert.AreEqual(tmp.Select(x => (int)x.NumBytes()).ToArray(), Enumerable.Repeat(Mb, 10).ToArray()); - - for (var i = 0; i < 10; ++i) - Assert.AreEqual(Ten1MBBuffers[i].Item2, tmp[i].ToBytes(), $"Buffer {i} are different"); - } - using (CreateTimer("OneGBBuffer")) - { - //Enumerable.Range(0, Gb).Select(i => (double)i).ToArray() - var bytes = BFast.WriteBFastToBytes(One1GBBuffer); - TestBFastBytes(bytes); - var tmp = BFast.ReadBFast(bytes).ToArray(); - Assert.AreEqual(1, tmp.Length); - Assert.AreEqual(tmp.Select(x => x.Name).ToArray(), new[] { "0" }); - Assert.AreEqual(tmp.Select(x => x.NumBytes()).ToArray(), Enumerable.Repeat((long)Gb, 1).ToArray()); - } - } - - public static BFastBuilder BFastWithSubs(int numBuffers, int numLevels, Func numBytes) - => Enumerable.Range(0, numBuffers).Aggregate(new BFastBuilder(), - (bld, i) => bld.Add(i.ToString(), - numLevels > 0 - ? BFastWithSubs(numBuffers, numLevels - 1, numBytes) - : BFastRoot(numBuffers, numBytes)) - ); - - public static BFastBuilder BFastRoot(int numBuffers, Func numBytes) - => Enumerable.Range(0, numBuffers).Aggregate(new BFastBuilder(), (bld, i) => bld.Add(i.ToString(), ByteArray(numBytes()).ToBuffer())); - - public static void ValidateBFast(byte[] buffer, BFastBuilder srcBuilder) - { - var bfast = BFast.ReadBFast(buffer).ToArray(); - - var names = srcBuilder.BufferNames().ToArray(); - var sizes = srcBuilder.BufferSizes().ToArray(); - var numBuffers = names.Count(); - // We should have the same number of buffers - AssertEquals(bfast.Length, numBuffers); - for (var i = 0; i < numBuffers; i++) - { - // Of equal size - AssertEquals(bfast[i].Name, names[i]); - AssertEquals(bfast[i].Data.Length, sizes[i]); - // And they might be sub-buffers - if (srcBuilder.Children[i].Item2 is BFastBuilder childBuilder) - ValidateBFast(bfast[i].ToBytes(), childBuilder); - } - } - - [Test] - public static void TestNestedBFast() - { - var random = new Random(1234567); - // Create a nested BFast structure 3 layers deep with randomly-sized buffers between 1 & 256 bytes size - var builder = BFastWithSubs(3, 3, () => random.Next(1, 256)); - // Create a buffer to recieve this structure; - var buffer = new byte[builder.GetSize()]; - var stream = new MemoryStream(buffer, true); - builder.Write(stream); - - // Now, lets try and deserialize these buffers: - ValidateBFast(buffer, builder); - } - - public static void AssertEquals(T x, T y) - { - if (!x.Equals(y)) - throw new Exception($"Expected value {x} but instead got {y}"); - } - - /// - /// This test cannot be run from the test runner, because the App.Config option - /// has to be enabled from within the host program. - /// - public static void ReallyBigTest() - { - var xs = new Vector3[500 * 1000 * 1000]; - for (var i = 0; i < xs.Length; ++i) - xs[i] = new Vector3(i, i, i); - var filePath = Path.Combine(Path.GetTempPath(), "really_big_test.bfast"); - using (var stream = File.OpenWrite(filePath)) - stream.WriteBFast(new[] { ("buffer", xs) }); - - var name = ""; - Vector3[] ys; - using (var stream = File.OpenRead(filePath)) - { - var buffers = BFast.ReadBFast(stream).ToArray(); - if (buffers.Length != 1) - throw new Exception($"Expected exactly one buffer, not {buffers.Length}"); - (name, ys) = (buffers[0].Name, buffers[1].AsArray()); - } - if (name != "buffer") - throw new Exception($"Expected name of buffer to be buffer not {name}"); - AssertEquals(xs.Length, ys.Length); - AssertEquals(xs[0], ys[0]); - AssertEquals(xs[1], ys[1]); - AssertEquals(xs[xs.Length - 1], ys[ys.Length - 1]); - } - } -} diff --git a/src/cs/bfast/Vim.BFast.Tests/BFastTests.cs b/src/cs/bfast/Vim.BFast.Tests/BFastTests.cs new file mode 100644 index 00000000..7e4a9742 --- /dev/null +++ b/src/cs/bfast/Vim.BFast.Tests/BFastTests.cs @@ -0,0 +1,576 @@ +using NUnit.Framework; +using NUnit.Framework.Constraints; +using System.Data; +using Vim.BFastLib.Core; +using Vim.Util.Tests; + +namespace Vim.BFastLib.Tests +{ + public class BFastTests + { + public static string ResultPath = Path.Combine(VimFormatRepoPaths.OutDir, "input.bfast"); + public static string ResultPath2 = Path.Combine(VimFormatRepoPaths.OutDir, "input.bfast"); + public static string ResidencePath = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + + BFast bfast; + + [SetUp] + public void Setup() + { + bfast = new BFast(); + + if (!Directory.Exists(VimFormatRepoPaths.OutDir)) + { + Directory.CreateDirectory(VimFormatRepoPaths.OutDir); + } + if (File.Exists(ResultPath)) + { + File.Delete(ResultPath); + } + if (File.Exists(ResultPath2)) + { + File.Delete(ResultPath2); + } + } + + private void TestBeforeAfter(Action method) + { + method(bfast); + + // Test that it also works after write/read + var next = new BFast(bfast.ToMemoryStream()); + method(next); + } + + private void TestBeforeAfter(Func method, IResolveConstraint constraint) + { + Assert.That(method(bfast), constraint); + + // Test that it also works after write/read + var next = new BFast(bfast.ToMemoryStream()); + Assert.That(method(next), constraint); + } + + private void TestBeforeAfterFile(Func method, IResolveConstraint constraint) + { + Assert.That(method(bfast), constraint); + using (var file = File.Open(ResultPath, FileMode.CreateNew)) + { + bfast.Write(file); + file.Seek(0, SeekOrigin.Begin); + + // Test that it also works after write/read + var next = new BFast(file); + Assert.That(method(next), constraint); + } + } + + #region empty + + [Test] + public void EmptyBFast_Has_No_Entries() + { + var bfast = new BFast(); + Assert.That(bfast.Entries.Count(), Is.EqualTo(0)); + } + + [Test] + public void EmptyBFast_GetArray_Returns_Null() + { + var bfast = new BFast(); + Assert.IsNull(bfast.GetArray("missing")); + } + + [Test] + public void EmptyBFast_GetBfast_Returns_Null() + { + var bfast = new BFast(); + Assert.IsNull(bfast.GetBFast("missing")); + } + + [Test] + public void EmptyBFast_GetEnumerable_Returns_Null() + { + var bfast = new BFast(); + Assert.IsNull(bfast.GetEnumerable("missing")); + } + + [Test] + public void EmptyBFast_Remove_Does_Nothing() + { + var bfast = new BFast(); + bfast.Remove("missing"); + } + + + [Test] + public void EmptyBFast_Writes_Header() + { + var bfast = new BFast(); + var stream = new MemoryStream(); + bfast.Write(stream); + + stream.Seek(0, SeekOrigin.Begin); + var raw = BFastHeader.FromStream(stream); + + Assert.That(raw.Ranges.Count, Is.EqualTo(0)); + } + #endregion + + #region enumerable + + [Test] + public void SetEnumerable_Adds_Entry() + { + bfast.SetEnumerable("A", () => new int[3] { 0, 1, 2 }); + TestBeforeAfter(b => b.Entries.Count(), Is.EqualTo(1)); + } + + [Test] + public void SetEnumerable_Then_GetEnumerable() + { + var expected = new int[3] { 0, 1, 2 }; + bfast.SetEnumerable("A", () => expected); + TestBeforeAfter(b => b.GetEnumerable("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetEnumerable_Then_GetEnumerable_Bytes() + { + bfast.SetEnumerable("A", () => new int[3] { 0, 1, 2 }); + var expected = (new int[3] { 0, 1, 2 }).SelectMany(i => BitConverter.GetBytes(i)); + TestBeforeAfter(b => b.GetEnumerable("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetEnumerable_Then_GetEnumerable_Float() + { + bfast.SetEnumerable("A", () => new int[3] { 0, 1, 2 }); + var expected = (new int[3] { 0, 1, 2 }).Select(i => BitConverter.Int32BitsToSingle(i)); + TestBeforeAfter(b => b.GetEnumerable("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetEnumerable_Then_GetArray() + { + bfast.SetEnumerable("A", () => new int[3] { 0, 1, 2 }); + var expected = new int[3] { 0, 1, 2 }; + TestBeforeAfter(b => b.GetArray("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetEnumerable_Then_GetArray_Bytes() + { + bfast.SetEnumerable("A", () => new int[3] { 0, 1, 2 }); + var expected = (new int[3] { 0, 1, 2 }).SelectMany(i => BitConverter.GetBytes(i)); + TestBeforeAfter(b => b.GetArray("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetEnumerable_Then_GetArray_Float() + { + bfast.SetEnumerable("A", () => new int[3] { 0, 1, 2 }); + var expected = (new int[3] { 0, 1, 2 }).Select(i => BitConverter.Int32BitsToSingle(i)); + // MemoryStream can't handle such size. + TestBeforeAfter(b => b.GetArray("A"), Is.EqualTo(expected)); + + } + + [Test] + public void SetEnumerable_Then_GetBFast_Throws() + { + bfast.SetEnumerable("A", () => new int[3] { 0, 1, 2 }); + TestBeforeAfter(b => { + Assert.That(() => b.GetBFast("A"), Throws.Exception); + }); + } + + [Test] + public void SetEnumerable_Then_GetBFast_ValidBytes() + { + var sub = new BFast(); + bfast.SetBFast("A", sub); + var bytes = bfast.GetArray("A"); + bfast.SetEnumerable("A",() => bytes); + + TestBeforeAfter(b => b.GetBFast("A"), Is.EqualTo(sub)); + } + + [Test, Explicit] + public void SetEnumerable_Then_GetEnumerable_Lots() + { + IEnumerable GetLots() + { + return Enumerable.Range(0, int.MaxValue).Concat(Enumerable.Range(0, 10)); + } + bfast.SetEnumerable("A", GetLots); + + TestBeforeAfterFile(b => b.GetEnumerable("A"), Is.EqualTo(GetLots())); + } + + #endregion + + #region array + [Test] + public void SetArray_Adds_Entry() + { + bfast.SetArray("A", new int[3] { 0, 1, 2 }); + TestBeforeAfter(b => b.Entries.Count(), Is.EqualTo(1)); + } + + [Test] + public void SetArray_Then_GetArray() + { + var array = new int[3] { 0, 1, 2 }; + bfast.SetArray("A", array); + TestBeforeAfter(b => b.GetArray("A"), Is.EqualTo(array)); + } + + [Test] + public void SetArray_Then_GetArray_Bytes() + { + var array = new int[3] { 0, 1, 2 }; + var expected = array.SelectMany(i => BitConverter.GetBytes(i)); + + bfast.SetArray("A", array); + TestBeforeAfter(b => bfast.GetArray("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetArray_Then_GetArray_Float() + { + var array = new int[3] { 0, 1, 2 }; + var expected = array.Select(i => BitConverter.Int32BitsToSingle(i)); + + bfast.SetArray("A", array); + TestBeforeAfter(b => bfast.GetArray("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetArray_Then_GetEnumerable() + { + var array = new int[3] { 0, 1, 2 }; + bfast.SetArray("A", array); + TestBeforeAfter(b => b.GetEnumerable("A"), Is.EqualTo(array)); + } + + [Test] + public void SetArray_Then_GetEnumerable_Bytes() + { + var array = new int[3] { 0, 1, 2 }; + var expected = array.SelectMany(i => BitConverter.GetBytes(i)); + + bfast.SetArray("A", array); + TestBeforeAfter(b => b.GetEnumerable("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetArray_Then_GetEnumerable_Float() + { + var array = new int[3] { 0, 1, 2 }; + var expected = array.Select(i => BitConverter.Int32BitsToSingle(i)); + + bfast.SetArray("A", array); + var result = bfast.GetEnumerable("A"); + } + + [Test] + public void SetArray_Then_GetBFast_Throws() + { + bfast.SetArray("A", new int[3] { 0, 1, 2 }); + TestBeforeAfter(b => { + Assert.That(() => b.GetBFast("A"), Throws.Exception); + }); + } + + [Test] + public void SetArray_Then_SetArray_Replaces() + { + var ints = new int[3] { 0, 1, 2 }; + var floats = new float[3] { 0.1f, 0.2f, 0.3f }; + bfast.SetArray("A", ints); + bfast.SetArray("A", floats); + TestBeforeAfter(b => { + Assert.That(b.GetArray("A"), Is.EqualTo(floats)); + Assert.That(b.GetArray("A"), Is.Not.EqualTo(ints)); + }); + } + + [Test] + public void SetArray_Then_SetBFast_Replaces() + { + bfast.SetArray("A", new int[3] { 0, 1, 2 }); + bfast.SetBFast("A", new BFast()); + TestBeforeAfter(b => + { + // That's the bfast read as an ints. + Assert.That(b.GetArray("A").Length, Is.GreaterThan(3)); + Assert.That(b.GetBFast("A"), Is.EqualTo(new BFast())); + }); + } + #endregion + + [Test] + public void SetBFast_Adds_Entry() + { + bfast.SetBFast("A", new BFast()); + TestBeforeAfter(b => b.Entries.Count(), Is.EqualTo(1)); + } + + [Test] + public void SetBFast_Then_GetBFast_Returns_Same() + { + var expected = new BFast(); + bfast.SetBFast("A", expected); + TestBeforeAfter(b => b.GetBFast("A"), Is.EqualTo(expected)); + } + + [Test] + public void SetBFast_Then_GetBFast_Nested() + { + using (var file = File.Open(ResidencePath, FileMode.Open)) + { + var (b1, b2) = (new BFast(), new BFast()); + b1.SetBFast("b2", b2); + bfast.SetBFast("b1", b1); + + var mem = bfast.ToMemoryStream(); + var r = new BFast(mem); + var r1 = r.GetBFast("b1"); + var r2 = r1.GetBFast("b2"); + + Assert.NotNull(r); + Assert.NotNull(r1); + Assert.NotNull(r2); + } + } + + #region compress + [Test] + public void Compression_Decompress_Uncompressed_Returns_Throws() + { + var expected = new BFast(); + bfast.SetBFast("A", expected); + TestBeforeAfter(b => + { + Assert.That(() => b.GetBFast("A", decompress: true), Throws.Exception); + }); + } + + [Test] + public void Compression_Get_Compressed_Returns_Null() + { + var expected = new BFast(); + bfast.SetBFast("A", expected, compress: true); + TestBeforeAfter(b => + { + Assert.That(() => b.GetBFast("A"), Throws.Exception); + }); + } + + [Test] + public void Compression_Get_Uncompressed_Works() + { + // This is tested by the bfast tests. + } + + [Test] + public void Compression_Decompress_Compressed_Works() + { + var ints = new int[3] { 0, 1, 2 }; + + var bfastA = new BFast(); + bfastA.SetArray("B", ints); + bfast.SetBFast("A", bfastA, compress: true); + + TestBeforeAfter((b) => + { + var result = b.GetBFast("A", decompress: true); + var b2 = result.GetArray("B"); + + Assert.That(result.Entries.Count(), Is.EqualTo(1)); + Assert.That(b2, Is.EqualTo(ints)); + }); + } + #endregion + + #region bfast + + [Test] + public void SetBFast_Then_SetBFast_Replaces() + { + var bfastA = new BFast(); + bfast.SetBFast("A", bfastA); + + var bfastB = new BFast(); + bfastB.SetArray("A", new int[] { 1, 2, 3 }); + bfast.SetBFast("A", bfastB); + + TestBeforeAfter((b) => + { + var result = b.GetBFast("A"); + Assert.That(bfastA, Is.Not.EqualTo(bfastB)); + Assert.That(result, Is.Not.EqualTo(bfastA)); + Assert.That(result, Is.EqualTo(bfastB)); + }); + } + + [Test] + public void SetBFast_Then_SetArray_Replaces() + { + var ints = new int[3] { 0, 1, 2 }; + bfast.SetBFast("A", new BFast()); + bfast.SetArray("A", ints); + TestBeforeAfter((b) => + { + Assert.That(() => b.GetBFast("A"), Throws.Exception); + Assert.That(b.GetArray("A"), Is.EqualTo(ints)); + }); + + } + #endregion + + [Test] + public void Remove_Missing_DoesNothing() + { + TestBeforeAfter((b) => + { + b.Remove("A"); + Assert.That(b.Entries.Count() == 0); + }); + } + + [Test] + public void Remove_Array() + { + bfast.SetArray("A", new int[3] { 0, 1, 2 }); + bfast.Remove("A"); + TestBeforeAfter((b) => + { + Assert.IsNull(b.GetArray("A")); + Assert.That(b.Entries.Count() == 0); + }); + } + + [Test] + public void Remove_BFast() + { + bfast.SetBFast("A", new BFast()); + bfast.Remove("A"); + + TestBeforeAfter((b) => + { + Assert.IsNull(bfast.GetBFast("A")); + Assert.That(bfast.Entries.Count() == 0); + }); + } + + [Test] + public void Removed_InChild_Not_Written() + { + using (var residence = File.OpenRead(ResidencePath)) + { + var input = new BFast(residence); + var geometry = input.GetBFast("geometry"); + geometry.Remove("g3d:vertex:position:0:float32:3"); + input.SetBFast("geometry", geometry); + input.Write(ResultPath); + } + + using (var stream = File.OpenRead(ResultPath)) + { + var bfast = new BFast(stream); + var geometry = bfast.GetBFast("geometry"); + Assert.That(bfast.Entries.Count() == 5); + Assert.That(geometry.Entries.Count() == 16); + Assert.IsNull(geometry.GetArray("g3d:vertex:position:0:float32:3")); + } + } + + [Test] + public void Write_Then_Read_NestedBFast() + { + var bfast = new BFast(); + var child = new BFast(); + var grandChild = new BFast(); + + bfast.SetBFast("child", child); + child.SetBFast("grandChild", grandChild); + bfast.Write(ResultPath); + + using (var stream = File.OpenRead(ResultPath)) + { + var other = new BFast(stream); + var child2 = other.GetBFast("child"); + var grandChild2 = child2.GetBFast("grandChild"); + + Assert.That(other.Entries.Count() == 1); + Assert.That(child2.Entries.Count() == 1); + Assert.That(grandChild2.Entries.Count() == 0); + } + } + + [Test] + public void Write_Then_Read_NestedBFast_WithArray() + { + var bfast = new BFast(); + var child = new BFast(); + var grandChild = new BFast(); + + bfast.SetBFast("child", child); + child.SetBFast("grandChild", grandChild); + grandChild.SetArray("A", new int[3] { 0, 1, 2 }); + + + bfast.Write(ResultPath); + using (var stream = File.OpenRead(ResultPath)) + { + var other = new BFast(stream); + var child2 = other.GetBFast("child"); + var grandChild2 = child2.GetBFast("grandChild"); + var result = grandChild2.GetArray("A"); + + Assert.That(other.Entries.Count() == 1); + Assert.That(child2.Entries.Count() == 1); + Assert.That(grandChild2.Entries.Count() == 1); + Assert.That(result, Is.EqualTo(new int[3] { 0, 1, 2 })); + } + } + + [Test] + public void Write_Then_Read_Mixed_Sources() + { + var basic = new BFast(); + var dummy = new MemoryStream(); + basic.SetArray("ints", new int[1] { 1 }); + basic.SetArray("floats", new float[1] { 2.0f }); + basic.Write(dummy); + + using (var residence = File.OpenRead(ResidencePath)) + { + dummy.Seek(0, SeekOrigin.Begin); + var input = new BFast(dummy); + + var inputResidence = new BFast(residence); + var output = new BFast(); + + output.SetBFast("input", input); + output.SetBFast("residence", inputResidence); + output.Write(ResultPath2); + } + + using (var stream = File.OpenRead(ResultPath2)) + { + var bfast = new BFast(stream); + var input = bfast.GetBFast("input"); + var residence = bfast.GetBFast("residence"); + var geometry = residence.GetBFast("geometry"); + + Assert.That(bfast.Entries.Count() == 2); + Assert.That(input.Entries.Count() == 2); + Assert.That(residence.Entries.Count() == 5); + Assert.That(geometry.Entries.Count() == 17); + } + } + } +} diff --git a/src/cs/bfast/Vim.BFast.Tests/Vim.BFast.Tests.csproj b/src/cs/bfast/Vim.BFast.Tests/Vim.BFast.Tests.csproj index 3ce7dc39..c7ddf233 100644 --- a/src/cs/bfast/Vim.BFast.Tests/Vim.BFast.Tests.csproj +++ b/src/cs/bfast/Vim.BFast.Tests/Vim.BFast.Tests.csproj @@ -1,18 +1,29 @@  - - net6.0 - false - + + net6.0 + enable + enable - - - - - + false + true + + + + True + + + - - - + + + + + + + + + + diff --git a/src/cs/bfast/Vim.BFast/BFast.cs b/src/cs/bfast/Vim.BFast/BFast.cs deleted file mode 100644 index 885fe582..00000000 --- a/src/cs/bfast/Vim.BFast/BFast.cs +++ /dev/null @@ -1,439 +0,0 @@ -/* - BFAST - Binary Format for Array Streaming and Transmission - Copyright 2019, VIMaec LLC - Copyright 2018, Ara 3D, Inc. - Usage licensed under terms of MIT License - https://github.com/vimaec/bfast - - The BFAST format is a simple, generic, and efficient representation of - buffers (arrays of binary data) with optional names. - - It can be used in place of a zip when compression is not required, or when a simple protocol - is required for transmitting data to/from disk, between processes, or over a network. -*/ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; - -namespace Vim.BFast -{ - /// - /// Callback function allows clients to control writing the data to the output stream - /// - public delegate long BFastWriterFn(Stream writingStream, int bufferIdx, string bufferName, long bytesToWrite); - - /// - /// Wraps an array of byte buffers encoding a BFast structure and provides validation and safe access to the memory. - /// The BFAST file/data format is structured as follows: - /// * File header - Fixed size file descriptor - /// * Ranges - An array of pairs of offsets that point to the begin and end of each data arrays - /// * Array data - All of the array data is contained in this section. - /// - public static class BFast - { - /// - /// Given a position in the stream, tells us where the the next aligned position will be, if it the current position is not aligned. - /// - public static long ComputeNextAlignment(long n) - => IsAligned(n) ? n : n + Constants.ALIGNMENT - (n % Constants.ALIGNMENT); - - /// - /// Given a position in the stream, computes how much padding is required to bring the value to an aligned point. - /// - public static long ComputePadding(long n) - => ComputeNextAlignment(n) - n; - - /// - /// Computes the padding requires after the array of BFastRanges are written out. - /// - /// - /// - public static long ComputePadding(BFastRange[] ranges) - => ComputePadding(BFastPreamble.Size + ranges.Length * BFastRange.Size); - - /// - /// Given a position in the stream, tells us whether the position is aligned. - /// - public static bool IsAligned(long n) - => n % Constants.ALIGNMENT == 0; - - /// - /// Writes n zero bytes. - /// - public static void WriteZeroBytes(this BinaryWriter bw, long n) - { - for (var i = 0L; i < n; ++i) - bw.Write((byte)0); - } - - /// - /// Checks that the stream (if seekable) is well aligned - /// - public static void CheckAlignment(Stream stream) - { - if (!stream.CanSeek) - return; - // TODO: Check with CD: Should we bail out here? This means that any - // alignment checks for a currently-writing stream are effectively ignored. - if (stream.Position == stream.Length) - return; - if (!IsAligned(stream.Position)) - throw new Exception($"Stream position {stream.Position} is not well aligned"); - } - - /// - /// Converts a collection of strings, into a null-separated byte[] array - /// - public static byte[] PackStrings(this IEnumerable strings) - { - var r = new List(); - foreach (var name in strings) - { - var bytes = Encoding.UTF8.GetBytes(name); - r.AddRange(bytes); - r.Add(0); - } - return r.ToArray(); - } - - /// - /// Converts a byte[] array encoding a collection of strings separate by NULL into an array of string - /// - public static string[] UnpackStrings(this byte[] bytes) - { - var r = new List(); - if (bytes.Length == 0) - return r.ToArray(); - var prev = 0; - for (var i = 0; i < bytes.Length; ++i) - { - if (bytes[i] == 0) - { - r.Add(Encoding.UTF8.GetString(bytes, prev, i - prev)); - prev = i + 1; - } - } - if (prev < bytes.Length) - r.Add(Encoding.UTF8.GetString(bytes, prev, bytes.Length - prev)); - return r.ToArray(); - } - - /// - /// Creates a BFAST structure, without any actual data buffers, from a list of sizes of buffers (not counting the name buffer). - /// Used as an intermediate step to create a BFAST. - /// - public static BFastHeader CreateBFastHeader(this long[] bufferSizes, string[] bufferNames) - { - if (bufferNames.Length != bufferSizes.Length) - throw new Exception($"The number of buffer sizes {bufferSizes.Length} is not equal to the number of buffer names {bufferNames.Length}"); - - var header = new BFastHeader - { - Names = bufferNames - }; - header.Preamble.Magic = Constants.Magic; - header.Preamble.NumArrays = bufferSizes.Length + 1; - - // Allocate the data for the ranges - header.Ranges = new BFastRange[header.Preamble.NumArrays]; - header.Preamble.DataStart = ComputeNextAlignment(header.Preamble.RangesEnd); - - var nameBufferLength = PackStrings(bufferNames).LongLength; - var sizes = (new[] { nameBufferLength }).Concat(bufferSizes).ToArray(); - - // Compute the offsets for the data buffers - var curIndex = header.Preamble.DataStart; - var i = 0; - foreach (var size in sizes) - { - curIndex = ComputeNextAlignment(curIndex); - Debug.Assert(IsAligned(curIndex)); - - header.Ranges[i].Begin = curIndex; - curIndex += size; - - header.Ranges[i].End = curIndex; - i++; - } - - // Finish with the header - // Each buffer we contain is padded to ensure the next one - // starts on alignment, so we pad our DataEnd to reflect this reality - header.Preamble.DataEnd = ComputeNextAlignment(curIndex); - - // Check that everything adds up - return header.Validate(); - } - - /// - /// Checks that the header values are sensible, and throws an exception otherwise. - /// - public static BFastPreamble Validate(this BFastPreamble preamble) - { - if (preamble.Magic != Constants.SameEndian && preamble.Magic != Constants.SwappedEndian) - throw new Exception($"Invalid magic number {preamble.Magic}"); - - if (preamble.DataStart < BFastPreamble.Size) - throw new Exception($"Data start {preamble.DataStart} cannot be before the file header size {BFastPreamble.Size}"); - - if (preamble.DataStart > preamble.DataEnd) - throw new Exception($"Data start {preamble.DataStart} cannot be after the data end {preamble.DataEnd}"); - - if (!IsAligned(preamble.DataEnd)) - throw new Exception($"Data end {preamble.DataEnd} should be aligned"); - - if (preamble.NumArrays < 0) - throw new Exception($"Number of arrays {preamble.NumArrays} is not a positive number"); - - if (preamble.NumArrays > preamble.DataEnd) - throw new Exception($"Number of arrays {preamble.NumArrays} can't be more than the total size"); - - if (preamble.RangesEnd > preamble.DataStart) - throw new Exception($"End of range {preamble.RangesEnd} can't be after data-start {preamble.DataStart}"); - - return preamble; - } - - /// - /// Checks that the header values are sensible, and throws an exception otherwise. - /// - public static BFastHeader Validate(this BFastHeader header) - { - var preamble = header.Preamble.Validate(); - var ranges = header.Ranges; - var names = header.Names; - - if (preamble.RangesEnd > preamble.DataStart) - throw new Exception($"Computed arrays ranges end must be less than the start of data {preamble.DataStart}"); - - if (ranges == null) - throw new Exception("Ranges must not be null"); - - var min = preamble.DataStart; - var max = preamble.DataEnd; - - for (var i = 0; i < ranges.Length; ++i) - { - var begin = ranges[i].Begin; - if (!IsAligned(begin)) - throw new Exception($"The beginning of the range is not well aligned {begin}"); - var end = ranges[i].End; - if (begin < min || begin > max) - throw new Exception($"Array offset begin {begin} is not in valid span of {min} to {max}"); - if (i > 0) - { - if (begin < ranges[i - 1].End) - throw new Exception($"Array offset begin {begin} is overlapping with previous array {ranges[i - 1].End}"); - } - - if (end < begin || end > max) - throw new Exception($"Array offset end {end} is not in valid span of {begin} to {max}"); - } - - if (names.Length < ranges.Length - 1) - throw new Exception($"Number of buffer names {names.Length} is not one less than the number of ranges {ranges.Length}"); - - return header; - } - - /// - /// Reads a BFAST from a file as a collection of named buffers. - /// - public static INamedBuffer[] Read(string filePath) - { - using (var stream = File.OpenRead(filePath)) - return Read(stream); - } - - /// - /// Reads a BFAST from a stream as a collection of named buffers. - /// - public static INamedBuffer[] Read(Stream stream) - => stream.ReadBFast().ToArray(); - - /// - /// Reads a BFAST buffer from a stream as a collection of named buffers. - /// This call limits the buffers to 2GB. - /// - public static IEnumerable ReadBFast(this Stream stream) - { - foreach (var br in stream.GetBFastBufferReaders()) - { - var s = br.Seek(); - yield return s.ReadArray((int)br.Size).ToNamedBuffer(br.Name); - } - } - - /// - /// Reads a BFAST from a stream as a collection of named buffers. - /// This call limits the buffers to 2GB. - /// - public static unsafe IEnumerable> ReadBFast(this Stream stream) where T : unmanaged - => stream.ReadBFast>((s, bufferName, bufferLength) - => s.ReadArray((int)(bufferLength / sizeof(T))).ToNamedBuffer(bufferName)) - .Select(item => item.Item2); - - /// - /// Reads a BFAST from a byte array as a collection of named buffers. - /// This call limits the buffers to 2GB. - /// - public static INamedBuffer[] ReadBFast(this byte[] bytes) - { - using (var stream = new MemoryStream(bytes)) - return ReadBFast(stream).ToArray(); - } - - /// - /// The total size required to put a BFAST in the header. - /// - public static long ComputeSize(long[] bufferSizes, string[] bufferNames) - => CreateBFastHeader(bufferSizes, bufferNames).Preamble.DataEnd; - - /// - /// Writes the BFAST header and name buffer to stream using the provided BinaryWriter. The BinaryWriter will be properly aligned by padding zeros - /// - public static BinaryWriter WriteBFastHeader(this Stream stream, BFastHeader header) - { - if (header.Ranges.Length != header.Names.Length + 1) - throw new Exception($"The number of ranges {header.Ranges.Length} must be equal to one more than the number of names {header.Names.Length}"); - var bw = new BinaryWriter(stream); - bw.Write(header.Preamble.Magic); - bw.Write(header.Preamble.DataStart); - bw.Write(header.Preamble.DataEnd); - bw.Write(header.Preamble.NumArrays); - foreach (var r in header.Ranges) - { - bw.Write(r.Begin); - bw.Write(r.End); - } - WriteZeroBytes(bw, ComputePadding(header.Ranges)); - - CheckAlignment(stream); - var nameBuffer = PackStrings(header.Names); - bw.Write(nameBuffer); - WriteZeroBytes(bw, ComputePadding(nameBuffer.LongLength)); - - CheckAlignment(stream); - return bw; - } - - /// - /// Enables a user to write a BFAST from an array of names, sizes, and a custom writing function. - /// The function will receive a BinaryWriter, the index of the buffer, and is expected to return the number of bytes written. - /// Simplifies the process of creating custom BinaryWriters, or writing extremely large arrays if necessary. - /// - public static void WriteBFast(this Stream stream, string[] bufferNames, long[] bufferSizes, BFastWriterFn onBuffer) - { - if (bufferSizes.Any(sz => sz < 0)) - throw new Exception("All buffer sizes must be zero or greater than zero"); - - if (bufferNames.Length != bufferSizes.Length) - throw new Exception($"The number of buffer names {bufferNames.Length} is not equal to the number of buffer sizes {bufferSizes}"); - - var header = CreateBFastHeader(bufferSizes, bufferNames); - stream.WriteBFast(header, bufferNames, bufferSizes, onBuffer); - } - - /// - /// Enables a user to write a BFAST from an array of names, sizes, and a custom writing function. - /// This is useful when the header is already computed. - /// - public static void WriteBFast(this Stream stream, BFastHeader header, string[] bufferNames, long[] bufferSizes, BFastWriterFn onBuffer) - { - stream.WriteBFastHeader(header); - CheckAlignment(stream); - stream.WriteBFastBody(header, bufferNames, bufferSizes, onBuffer); - } - - /// - /// Must be called after "WriteBFastHeader" - /// Enables a user to write the contents of a BFAST from an array of names, sizes, and a custom writing function. - /// The function will receive a BinaryWriter, the index of the buffer, and is expected to return the number of bytes written. - /// Simplifies the process of creating custom BinaryWriters, or writing extremely large arrays if necessary. - /// - public static void WriteBFastBody(this Stream stream, BFastHeader header, string[] bufferNames, long[] bufferSizes, BFastWriterFn onBuffer) - { - CheckAlignment(stream); - - if (bufferSizes.Any(sz => sz < 0)) - throw new Exception("All buffer sizes must be zero or greater than zero"); - - if (bufferNames.Length != bufferSizes.Length) - throw new Exception($"The number of buffer names {bufferNames.Length} is not equal to the number of buffer sizes {bufferSizes}"); - - // Then passes the binary writer for each buffer: checking that the correct amount of data was written. - for (var i = 0; i < bufferNames.Length; ++i) - { - CheckAlignment(stream); - var nBytes = bufferSizes[i]; - var pos = stream.CanSeek ? stream.Position : 0; - var nWrittenBytes = onBuffer(stream, i, bufferNames[i], nBytes); - if (stream.CanSeek) - { - if (stream.Position - pos != nWrittenBytes) - throw new NotImplementedException($"Buffer:{bufferNames[i]}. Stream movement {stream.Position - pos} does not reflect number of bytes claimed to be written {nWrittenBytes}"); - } - - if (nBytes != nWrittenBytes) - throw new Exception($"Number of bytes written {nWrittenBytes} not equal to expected bytes{nBytes}"); - var padding = ComputePadding(nBytes); - for (var j = 0; j < padding; ++j) - stream.WriteByte(0); - CheckAlignment(stream); - } - } - - public static unsafe long ByteSize(this T[] self) where T : unmanaged - => self.LongLength * sizeof(T); - - public static unsafe void WriteBFast(this Stream stream, IEnumerable<(string, T[])> buffers) where T : unmanaged - { - var xs = buffers.ToArray(); - BFastWriterFn writerFn = (writer, index, name, size) => - { - var initPosition = writer.Position; - writer.Write(xs[index].Item2); - return writer.Position - initPosition; - }; - - stream.WriteBFast( - xs.Select(b => b.Item1), - xs.Select(b => b.Item2.ByteSize()), - writerFn); - } - - public static void WriteBFast(this Stream stream, IEnumerable bufferNames, IEnumerable bufferSizes, BFastWriterFn onBuffer) - => WriteBFast(stream, bufferNames.ToArray(), bufferSizes.ToArray(), onBuffer); - - public static byte[] WriteBFastToBytes(IEnumerable bufferNames, IEnumerable bufferSizes, BFastWriterFn onBuffer) - { - // NOTE: we can't call "WriteBFast(Stream ...)" directly because it disposes the stream before we can convert it to an array - using (var stream = new MemoryStream()) - { - WriteBFast(stream, bufferNames.ToArray(), bufferSizes.ToArray(), onBuffer); - return stream.ToArray(); - } - } - - public static void WriteBFastToFile(string filePath, IEnumerable bufferNames, IEnumerable bufferSizes, BFastWriterFn onBuffer) - => File.OpenWrite(filePath).WriteBFast(bufferNames, bufferSizes, onBuffer); - - public static unsafe byte[] WriteBFastToBytes(this (string Name, T[] Data)[] buffers) where T : unmanaged - => WriteBFastToBytes( - buffers.Select(b => b.Name), - buffers.Select(b => b.Data.LongLength * sizeof(T)), - (writer, index, name, size) => - { - var initPosition = writer.Position; - writer.Write(buffers[index].Data); - return writer.Position - initPosition; - }); - - public static BFastBuilder ToBFastBuilder(this IEnumerable buffers) - => new BFastBuilder().Add(buffers); - } -} diff --git a/src/cs/bfast/Vim.BFast/BFast/BFast.cs b/src/cs/bfast/Vim.BFast/BFast/BFast.cs new file mode 100644 index 00000000..880251b3 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/BFast/BFast.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Vim.BFastLib.Core; + +namespace Vim.BFastLib +{ + /// + /// Main API to read and write bfast content. + /// + public class BFast : IBFastNode + { + private readonly Dictionary _children = new Dictionary(); + + /// + /// Returns all buffer names in this bfast. + /// + public IEnumerable Entries => _children.Keys; + private IEnumerable<(string name, IWritable buffer)> Writables => _children.Select(kvp => (kvp.Key, kvp.Value as IWritable)); + + public BFast() { } + public BFast(Stream stream) + { + var nodes = GetBFastNodes(stream); + _children = nodes.ToDictionary(c => c.name, c => new CompressibleNode(c.value)); + } + + /// + /// Sets or overrides a bfast value at given name. + /// + public void SetBFast(string name, BFast bfast, bool compress = false) + { + if (bfast == null) + { + _children.Remove(name); + return; + } + + _children[name] = new CompressibleNode(bfast, compress); + } + + /// + /// Sets or overrides an enumerable value at given name. + /// + public void SetEnumerable(string name, Func> enumerable) where T : unmanaged + { + if (enumerable == null) + { + _children.Remove(name); + return; + } + _children[name] = new CompressibleNode(new BFastEnumerableNode(enumerable)); + } + + /// + /// Sets or overrides an array value at given name. + /// + public void SetArray(string name, T[] array) where T : unmanaged + { + if (array == null) + { + _children.Remove(name); + return; + } + _children[name] = new CompressibleNode(new BFastArrayNode(array)); + } + + /// + /// Tries to interpret the data at given name as a BFast and returns it. + /// Will throw if the data is not a bfast or if decompress doesnt match compression. + /// + public BFast GetBFast(string name, bool decompress = false) + { + var node = GetNode(name); + if (node == null) return null; + var n = node.GetNode(decompress); + return n.AsBFast(); + } + + /// + /// Tries to cast the data at given name as an enumerable of type T. + /// Will throw if the data cannot be cast. + /// + public IEnumerable GetEnumerable(string name) where T : unmanaged + { + if (!_children.ContainsKey(name)) return null; + return _children[name].GetNode().AsEnumerable(); + } + + /// + /// Tries to cast the data at given name as an array of type T. + /// Will throw if the data cannot be cast. + /// + public T[] GetArray(string name) where T : unmanaged + { + if (!_children.ContainsKey(name)) return null; + return _children[name].GetNode().AsArray(); + } + + private CompressibleNode GetNode(string name) + => _children.TryGetValue(name, out var value) ? value : null; + + /// + /// Remove the value at name so it won't be written. + /// + public void Remove(string name) + => _children.Remove(name); + + /// + /// Writes the current state to a stream using bfast format. + /// + public void Write(Stream stream) + { + var list = Writables.OrderBy(kvp => kvp.name).ToList(); + var strings = list.Select(n => n.name).ToArray(); + var buffers = list.Select(n => n.buffer).ToArray(); + var writer = new BFastWriter(strings, buffers); + writer.Write(stream); + } + + /// + /// Writes the current state to a new file using bfast format. + /// + public void Write(string path) + { + using (var file = new FileStream(path, FileMode.Create)) + { + Write(file); + } + } + + BFast IBFastNode.AsBFast() + { + return this; + } + + T[] IBFastNode.AsArray() + { + using (var mem = ToMemoryStream()) + { + return mem.ReadArray(); + } + } + + IEnumerable IBFastNode.AsEnumerable() + { + return (this as IBFastNode).AsArray(); + } + + private static IEnumerable<(string name, BFastStreamNode value)> GetBFastNodes(Stream stream) + { + var offset = stream.Position; + var raw = BFastHeader.FromStream(stream); + foreach (var kvp in raw.Ranges) + { + var node = new BFastStreamNode( + stream, + kvp.Value.OffsetBy(offset) + ); + + yield return (kvp.Key, node); + } + } + + /// + /// Writes the current bfast to a new memory streams + /// The stream is returned at position 0. + /// + public MemoryStream ToMemoryStream() + { + var stream = new MemoryStream(); + Write(stream); + stream.Seek(0, SeekOrigin.Begin); + return stream; + } + + public override bool Equals(object obj) + { + if (obj is BFast) + { + return Equals((BFast)obj); + } + return false; + } + + public bool Equals(BFast other) + { + var a = (this as IBFastNode).AsEnumerable(); + var b = (other as IBFastNode).AsEnumerable(); + return a.SequenceEqual(b); + } + + public override int GetHashCode() => (this as IBFastNode).AsEnumerable().GetHashCode(); + } +} + diff --git a/src/cs/bfast/Vim.BFast/BFast/BFastArrayNode.cs b/src/cs/bfast/Vim.BFast/BFast/BFastArrayNode.cs new file mode 100644 index 00000000..cc6a06da --- /dev/null +++ b/src/cs/bfast/Vim.BFast/BFast/BFastArrayNode.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Vim.BFastLib.Core; + +namespace Vim.BFastLib +{ + public class BFastArrayNode : IBFastNode where TData : unmanaged + { + private readonly TData[] _array; + + public BFastArrayNode(TData[] array) + { + _array = array; + } + + public T[] AsArray() where T : unmanaged + { + if (typeof(T) == typeof(TData)) + { + return _array as T[]; + } + return _array.Cast(); + } + + public BFast AsBFast() + { + try + { + return new BFast(_array.ToMemoryStream()); + } + catch (Exception e) + { + throw new Exception("Array data is not a valid BFast.", e); + } + } + + public IEnumerable AsEnumerable() where T: unmanaged { + return (_array as IEnumerable).Cast(); + } + + public void Write(Stream stream) + { + stream.Write(_array); + } + } +} diff --git a/src/cs/bfast/Vim.BFast/BFast/BFastEnumerableNode.cs b/src/cs/bfast/Vim.BFast/BFast/BFastEnumerableNode.cs new file mode 100644 index 00000000..aa6f4eb7 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/BFast/BFastEnumerableNode.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Vim.BFastLib.Core; + +namespace Vim.BFastLib +{ + public class BFastEnumerableNode : IBFastNode where TNode : unmanaged + { + // We use a Func to prevent cases where the given IEnumerable can't iterated twice. + private readonly Func> _source; + + public BFastEnumerableNode(Func> source) + { + _source = source; + } + + public T[] AsArray() where T : unmanaged + { + if (typeof(T) == typeof(TNode)) + { + return _source().Cast().ToArray(); + } + else + { + return _source().Cast().ToArray(); + } + } + + public BFast AsBFast() + { + try + { + return new BFast(_source().ToMemoryStream()); + } + catch (Exception e) + { + throw new Exception("Enumerable data is not a valid BFast", e); + } + } + public IEnumerable AsEnumerable() where T : unmanaged + { + if (typeof(T) == typeof(TNode)) + { + return _source().Cast(); + } + else + { + return _source().Cast(); + } + } + + public void Write(Stream stream) + { + stream.Write(_source()); + } + } +} diff --git a/src/cs/bfast/Vim.BFast/BFast/BFastHelpers.cs b/src/cs/bfast/Vim.BFast/BFast/BFastHelpers.cs new file mode 100644 index 00000000..0aa910a0 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/BFast/BFastHelpers.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Vim.BFastLib +{ + public static class BFastHelpers + { + /// + /// Opens a file as a BFast, applies func to it and closes the file. + /// + public static T Read(string path, Func func) + { + using (var file = new FileStream(path, FileMode.Open)) + { + var bfast = new BFast(file); + return func(bfast); + } + } + } + + public static class BFastExtensions + { + /// + /// Returns an enumerable of all nodes of the BFast as NamedBuffers. + /// + public static IEnumerable ToNamedBuffers(this BFast bfast) + { + return bfast.Entries.Select(name => bfast.GetArray(name).ToNamedBuffer(name)); + } + + /// + /// Writes the current bfast to a new memory streams + /// The stream is returned at position 0. + /// + public static MemoryStream ToMemoryStream(this IBFastNode bfast) + { + var stream = new MemoryStream(); + bfast.Write(stream); + stream.Seek(0, SeekOrigin.Begin); + return stream; + } + } +} + diff --git a/src/cs/bfast/Vim.BFast/BFast/BFastStreamNode.cs b/src/cs/bfast/Vim.BFast/BFast/BFastStreamNode.cs new file mode 100644 index 00000000..457b8470 --- /dev/null +++ b/src/cs/bfast/Vim.BFast/BFast/BFastStreamNode.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Vim.BFastLib.Core; + +namespace Vim.BFastLib +{ + public class BFastStreamNode : IBFastNode + { + private readonly Stream _stream; + private readonly BFastRange _range; + + public BFastStreamNode(Stream stream, BFastRange range) + { + _stream = stream; + _range = range; + } + + public BFast AsBFast() + { + _stream.Seek(_range.Begin, SeekOrigin.Begin); + try + { + return new BFast(_stream); + } + catch (Exception e) + { + throw new Exception("Requested data is not a valid BFast or is compressed and needs decompression.", e); + } + } + + public T[] AsArray() where T : unmanaged + { + _stream.Seek(_range.Begin, SeekOrigin.Begin); + return _stream.ReadArrayBytes(_range.Count); + } + + public IEnumerable AsEnumerable() where T : unmanaged + { + _stream.Seek(_range.Begin, SeekOrigin.Begin); + return _stream.ReadEnumerableByte(_range.Count); + } + + public void Write(Stream stream) + { + _stream.Seek(_range.Begin, SeekOrigin.Begin); + _stream.CopySome(stream, (int)_range.Count); + } + } +} diff --git a/src/cs/bfast/Vim.BFast/BFast/CompressibleNode.cs b/src/cs/bfast/Vim.BFast/BFast/CompressibleNode.cs new file mode 100644 index 00000000..7a85eeba --- /dev/null +++ b/src/cs/bfast/Vim.BFast/BFast/CompressibleNode.cs @@ -0,0 +1,83 @@ +using System.IO; +using System.IO.Compression; +using Vim.BFastLib.Core; + +namespace Vim.BFastLib +{ + /// + /// A wrapper around a IBFastNode that manages writing and reading using compression. + /// + public class CompressibleNode : IWritable + { + private readonly IBFastNode _node; + private readonly bool _compress; + + public CompressibleNode(IBFastNode node, bool compress = false) + { + _node = node; + _compress = compress; + } + + public void Write(Stream stream) + { + if (_compress) + { + WriteCompress(stream); + } + else + { + _node.Write(stream); + } + } + + /// + /// Returns the node after it is decompressed if needed. + /// Will throw if decompress argument doesnt match compression state. + /// + public IBFastNode GetNode(bool decompress = false) + { + if (decompress) + { + if (_node is BFastStreamNode) + { + return Decompress(); + } + if (!_compress) + { + throw new System.Exception("Cannot uncompress non-compressed data."); + } + return _node; + } + if(_compress) + { + throw new System.Exception("Compressed data needs to be decompressed."); + } + return _node; + } + + private IBFastNode Decompress() + { + // This memory stream is not disposed. But it's ok. + // It really is just an array under the hood. + // https://stackoverflow.com/questions/4274590/memorystream-close-or-memorystream-dispose + var output = new MemoryStream(); + + using (var input = _node.ToMemoryStream()) + using (var compress = new DeflateStream(input, CompressionMode.Decompress, true)) + { + compress.CopyTo(output); + output.Seek(0, SeekOrigin.Begin); + return new BFastStreamNode(output, output.FullRange()); + } + } + + private void WriteCompress(Stream stream) + { + using (var input = _node.ToMemoryStream()) + using (var compress = new DeflateStream(stream, CompressionMode.Compress, true)) + { + input.CopyTo(compress); + } + } + } +} diff --git a/src/cs/bfast/Vim.BFast/BFast/IBFastNode.cs b/src/cs/bfast/Vim.BFast/BFast/IBFastNode.cs new file mode 100644 index 00000000..5d27c59e --- /dev/null +++ b/src/cs/bfast/Vim.BFast/BFast/IBFastNode.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.IO; + +namespace Vim.BFastLib +{ + public interface IWritable + { + /// + /// Writes the current data to the given stream. + /// + void Write(Stream stream); + } + + public interface IBFastNode : IWritable + { + /// + /// Tries to cast node data as an array of T. + /// + T[] AsArray() where T : unmanaged; + + /// + /// Tries to cast node data as an enumerable of T. + /// AsEnumerable() where T : unmanaged; + + /// + /// Tries to interpret node data as a BFast. + /// - /// Represents a BFAST buffer whose stream can be read after calling Seek(). - /// - public class BFastBufferReader - { - /// - /// The seekable stream from which the buffer can be read. - /// - private readonly Stream _stream; - - /// - /// The start position of the buffer in the stream. - /// - private readonly long _startPosition; - - /// - /// The size in bytes of the buffer. - /// - public readonly long Size; - - /// - /// The buffer name. - /// - public readonly string Name; - - /// - /// Deconstruct operator - /// - public void Deconstruct(out string name, out long size) - => (name, size) = (Name, Size); - - /// - /// Constructor. - /// - public BFastBufferReader(Stream stream, string name, long startPosition, long size) - { - _stream = stream; - _startPosition = startPosition; - Size = size; - Name = name; - } - - /// - /// Seeks to the start of the BFAST buffer and returns the stream. - /// - public Stream Seek() - { - _stream.Seek(_startPosition, SeekOrigin.Begin); - BFast.CheckAlignment(_stream); - return _stream; - } - } - - public static class BFastBufferReaderExtensions - { - /// - /// Reads the preamble, the ranges, and the names of the rest of the buffers. - /// - public static BFastHeader ReadBFastHeader(this Stream stream) - { - var r = new BFastHeader(); - var br = new BinaryReader(stream); - - if (stream.Length - stream.Position < sizeof(long) * 4) - throw new Exception("Stream too short"); - - r.Preamble = new BFastPreamble - { - Magic = br.ReadInt64(), - DataStart = br.ReadInt64(), - DataEnd = br.ReadInt64(), - NumArrays = br.ReadInt64(), - }.Validate(); - - r.Ranges = stream.ReadArray((int)r.Preamble.NumArrays); - - var padding = BFast.ComputePadding(r.Ranges); - br.ReadBytes((int)padding); - BFast.CheckAlignment(br.BaseStream); - - var nameBytes = br.ReadBytes((int)r.Ranges[0].Count); - r.Names = nameBytes.UnpackStrings(); - - padding = BFast.ComputePadding(r.Ranges[0].End); - br.ReadBytes((int)padding); - BFast.CheckAlignment(br.BaseStream); - - return r.Validate(); - } - - /// - /// Returns a list of BFAST buffer readers in the stream. - /// Assumes the stream's current position designates a BFAST header. - /// - public static IReadOnlyList GetBFastBufferReaders( - this Stream stream, - Func filterFn = null) - { - var result = new List(); - - using (var seekContext = new SeekContext(stream)) - { - // Read the header - var header = stream.ReadBFastHeader(); - BFast.CheckAlignment(stream); - - // Create a BFastBufferReader for each range. - for (var i = 1; i < header.Ranges.Length; ++i) - { - var range = header.Ranges[i]; - var name = header.Names[i - 1]; - - var startSeekPosition = seekContext.OriginalSeekPosition + range.Begin; - var size = range.End - range.Begin; - - var bfastBufferReader = new BFastBufferReader(seekContext.Stream, name, startSeekPosition, size); - - if (filterFn?.Invoke(bfastBufferReader) ?? true) - { - result.Add(bfastBufferReader); - } - } - } - - return result; - } - - /// - /// Returns a BFAST buffer reader corresponding to the given buffer name. - /// Returns null if the given buffer name was not found or if the buffer name is null or empty. - /// - public static BFastBufferReader GetBFastBufferReader(this Stream stream, string bufferName) - => string.IsNullOrEmpty(bufferName) - ? null - : stream.GetBFastBufferReaders(br => br.Name == bufferName).FirstOrDefault(); - - /// - /// Reads a BFAST stream and returns a list of labeled results. - /// - public static List<(string Label, T Result)> ReadBFast( - this Stream stream, - Func onBuffer) - { - var result = new List<(string, T)>(); - - foreach (var br in stream.GetBFastBufferReaders()) - { - var name = br.Name; - var s = br.Seek(); - result.Add((name, onBuffer(s, name, br.Size))); - } - - return result; - } - - /// - /// Returns a named buffer corresponding to the given bufferName. Returns null if no buffer name is found. - /// This call limits the buffers to 2GB. - /// - public static NamedBuffer ReadBFastBuffer(this Stream stream, string bufferName) where T : unmanaged - { - var br = stream.GetBFastBufferReader(bufferName); - if (br == null) - return null; - - var s = br.Seek(); - return s.ReadArray((int)br.Size).ToNamedBuffer(br.Name); - } - } -} diff --git a/src/cs/bfast/Vim.BFast/BFastBuilder.cs b/src/cs/bfast/Vim.BFast/BFastBuilder.cs deleted file mode 100644 index 7c39221b..00000000 --- a/src/cs/bfast/Vim.BFast/BFastBuilder.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; - -namespace Vim.BFast -{ - /// - /// Anything that can be added to a BFAST must have a size and write to a stream. - /// - public interface IBFastComponent - { - long GetSize(); - void Write(Stream stream); - } - - /// - /// A wrapper around a buffer so that it can be used as a BFAST component - /// - public class BufferAsBFastComponent : IBFastComponent - { - public BufferAsBFastComponent(IBuffer buffer) - => Buffer = buffer; - public IBuffer Buffer { get; } - public void Write(Stream stream) => stream.Write(Buffer); - public long GetSize() => Buffer.NumBytes(); - } - - /// - /// Used to build BFASTs incrementally that contain named buffers and/or other BFASTs. - /// - public class BFastBuilder : IBFastComponent - { - public BFastHeader Header { get; private set; } - public long GetSize() => GetOrComputeHeader().Preamble.DataEnd; - - public List<(string, IBFastComponent)> Children { get; } = new List<(string, IBFastComponent)>(); - - public void Write(Stream stream) - => stream.WriteBFast(GetOrComputeHeader(), - BufferNames().ToArray(), - BufferSizes().ToArray(), - OnBuffer); - - public void Write(string filePath) - { - using (var stream = File.OpenWrite(filePath)) - Write(stream); - } - - public long OnBuffer(Stream stream, int index, string name, long size) - { - var (bufferName, x) = Children[index]; - Debug.Assert(name == bufferName); - Debug.Assert(size != GetSize()); - Debug.Assert(size == x.GetSize()); - x.Write(stream); - return size; - } - - public BFastHeader GetOrComputeHeader() - => Header ?? (Header = BFast.CreateBFastHeader( - BufferSizes().ToArray(), BufferNames().ToArray())); - - private BFastBuilder _add(string name, IBFastComponent component) - { - Header = null; - Children.Add((name, component)); - return this; - } - - public BFastBuilder Add(string name, IBFastComponent component) - => _add(name, component); - - public BFastBuilder Add(string name, IBuffer buffer) - => _add(name, new BufferAsBFastComponent(buffer)); - - public BFastBuilder Add(INamedBuffer buffer) - => Add(buffer.Name, buffer); - - public BFastBuilder Add(IEnumerable buffers) - => buffers.Aggregate(this, (x, y) => x.Add(y)); - - public BFastBuilder Add(string name, IEnumerable buffers) - => Add(name, new BFastBuilder().Add(buffers)); - - public IEnumerable BufferNames() - => Children.Select(x => x.Item1); - - public IEnumerable BufferSizes() - => Children.Select(x => x.Item2.GetSize()); - } -} diff --git a/src/cs/bfast/Vim.BFast/BFastStructs.cs b/src/cs/bfast/Vim.BFast/BFastStructs.cs deleted file mode 100644 index 46cb0dde..00000000 --- a/src/cs/bfast/Vim.BFast/BFastStructs.cs +++ /dev/null @@ -1,113 +0,0 @@ -/* - BFAST - Binary Format for Array Streaming and Transmission - Copyright 2019, VIMaec LLC - Copyright 2018, Ara 3D, Inc. - Usage licensed under terms of MIT License - https://github.com/vimaec/bfast - - The BFAST format is a simple, generic, and efficient representation of - buffers (arrays of binary data) with optional names. - - It can be used in place of a zip when compression is not required, or when a simple protocol - is required for transmitting data to/from disk, between processes, or over a network. -*/ - -using System.Linq; -using System.Runtime.InteropServices; - -namespace Vim.BFast -{ - /// - /// This contains the BFAST data loaded or written from disk. - /// - public class BFastHeader - { - public BFastPreamble Preamble = new BFastPreamble(); - public BFastRange[] Ranges; - public string[] Names; - - public override bool Equals(object o) - => o is BFastHeader other && Equals(other); - - public bool Equals(BFastHeader other) - => Preamble.Equals(other.Preamble) && - Ranges.Length == other.Ranges.Length && - Ranges.Zip(other.Ranges, (x, y) => x.Equals(y)).All(x => x) && - Names.Zip(other.Names, (x, y) => x.Equals(y)).All(x => x); - } - - /// - /// Constants. - /// - public static class Constants - { - public const long Magic = 0xBFA5; - - // https://en.wikipedia.org/wiki/Endianness - public const long SameEndian = Magic; - public const long SwappedEndian = 0xA5BFL << 48; - - /// - /// Data arrays are aligned to 64 bytes, so that they can be cast directly to AVX-512 registers. - /// This is useful for efficiently working with floating point data. - /// - public const long ALIGNMENT = 64; - } - - /// - /// This tells us where a particular array begins and ends in relation to the beginning of a file. - /// * Begin must be less than or equal to End. - /// * Begin must be greater than or equal to DataStart - /// * End must be less than or equal to DataEnd - /// - [StructLayout(LayoutKind.Explicit, Pack = 8, Size = 16)] - public struct BFastRange - { - [FieldOffset(0)] public long Begin; - [FieldOffset(8)] public long End; - - public long Count => End - Begin; - public static long Size = 16; - - public override bool Equals(object x) - => x is BFastRange other && Equals(other); - - public bool Equals(BFastRange other) - => Begin == other.Begin && End == other.End; - } - - /// - /// The header contains a magic number, the begin and end indices of data, and the number of arrays. - /// - [StructLayout(LayoutKind.Explicit, Pack = 8, Size = 32)] - public struct BFastPreamble - { - [FieldOffset(0)] public long Magic; // Either Constants.SameEndian or Constants.SwappedEndian depending on endianess of writer compared to reader. - [FieldOffset(8)] public long DataStart; // <= file size and >= ArrayRangesEnd and >= FileHeader.ByteCount - [FieldOffset(16)] public long DataEnd; // >= DataStart and <= file size - [FieldOffset(24)] public long NumArrays; // number of arrays - - /// - /// This is where the array ranges are finished. - /// Must be less than or equal to DataStart. - /// Must be greater than or equal to FileHeader.ByteCount - /// - public long RangesEnd => Size + NumArrays * 16; - - /// - /// The size of the FileHeader structure - /// - public static long Size = 32; - - /// - /// Returns true if the producer of the BFast file has the same endianness as the current library - /// - public bool SameEndian => Magic == Constants.SameEndian; - - public override bool Equals(object x) - => x is BFastPreamble other && Equals(other); - - public bool Equals(BFastPreamble other) - => Magic == other.Magic && DataStart == other.DataStart && DataEnd == other.DataEnd && NumArrays == other.NumArrays; - }; -} diff --git a/src/cs/bfast/Vim.BFast/BufferExtensions.cs b/src/cs/bfast/Vim.BFast/Buffers/BufferExtensions.cs similarity index 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/Vim.G3d.AssimpWrapper.csproj b/src/cs/g3d/Vim.G3d.AssimpWrapper/Vim.G3d.AssimpWrapper.csproj index afd6d77d..3b567b9f 100644 --- a/src/cs/g3d/Vim.G3d.AssimpWrapper/Vim.G3d.AssimpWrapper.csproj +++ b/src/cs/g3d/Vim.G3d.AssimpWrapper/Vim.G3d.AssimpWrapper.csproj @@ -12,5 +12,10 @@ - + + + True + + + diff --git a/src/cs/g3d/Vim.G3d.Tests/G3dTestUtils.cs b/src/cs/g3d/Vim.G3d.Tests/G3dTestUtils.cs deleted file mode 100644 index 0a28e014..00000000 --- a/src/cs/g3d/Vim.G3d.Tests/G3dTestUtils.cs +++ /dev/null @@ -1,137 +0,0 @@ -using Assimp; -using NUnit.Framework; -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using Vim.G3d.AssimpWrapper; -using Vim.LinqArray; - -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) - { - //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}"); - } - - 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..fa9f38ed 100644 --- a/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs +++ b/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs @@ -4,13 +4,14 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Vim.BFastLib; using Vim.G3d.AssimpWrapper; using Vim.LinqArray; using Vim.Math3d; namespace Vim.G3d.Tests { - [TestFixture, Ignore("Ignored until the new version is ready")] + [TestFixture] public static class G3dTests { public class FileLoadData @@ -37,6 +38,23 @@ public FileLoadData(string filePath) 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"); + + [SetUp] + public static void Setup() + { + if (!Directory.Exists(RootFolder)) + { + Directory.CreateDirectory(RootFolder); + } + if (!Directory.Exists(TestInputFolder)) + { + Directory.CreateDirectory(TestInputFolder); + } + if (!Directory.Exists(TestOutputFolder)) + { + Directory.CreateDirectory(TestOutputFolder); + } + } public static IEnumerable GetInputFiles() => Directory.GetFiles(TestInputFolder, "*.*", SearchOption.AllDirectories); @@ -66,16 +84,6 @@ public static void ValidateSameG3D(G3D g1, G3D g2) } } - [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() @@ -128,7 +136,7 @@ public static void OpenAndConvertAssimpFiles() f.G3DFile = new FileInfo(outputFilePath); f.MSecToSaveG3d = Util.GetMSecElapsed(() => - f.G3d.Write(outputFilePath)); + f.G3d.ToBFast().Write(outputFilePath)); } catch (Exception e) { @@ -215,8 +223,8 @@ public static void TriangleTest() .Add(materialIndices.ToIArray().ToFaceMaterialAttribute()) .ToG3D(); - var bytes = g3d.WriteToBytes(); - var g = G3D.Read(bytes); + var bfast = g3d.ToBFast(); + var g = G3D.Read(bfast); Assert.IsNotNull(g); @@ -233,84 +241,13 @@ public static void TriangleTest() } [Test] - public static void QuadAndCopyTest() + public static void UnexpectedAttributes_Are_Ignored() { - // 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()); - } - - [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 bfast = new BFast(); + bfast.SetArray("g3d:instance:potato:0:int32:1", new int[] { 5 }); + var g = G3D.Read(bfast); 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()); } @@ -332,15 +269,16 @@ public static void BigFileTest() 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); + + var expectedG3d = bldr.ToG3D(); + Assert.AreEqual(nVerts, expectedG3d.NumVertices); + var bfast = expectedG3d.ToBFast(); + var resultG3d = G3D.Read(bfast); + + ValidateSameG3D(expectedG3d, resultG3d); } - [Test] + [Test, Explicit] [Platform(Exclude = "Linux,Unix", Reason = "AssimpNet is failing to load its dependency on 'libdl.so'.")] public static void TestWriters() { diff --git a/src/cs/g3d/Vim.G3d.Tests/Properties/Resources.Designer.cs b/src/cs/g3d/Vim.G3d.Tests/Properties/Resources.Designer.cs index 2f5f4dfd..dba826c3 100644 --- a/src/cs/g3d/Vim.G3d.Tests/Properties/Resources.Designer.cs +++ b/src/cs/g3d/Vim.G3d.Tests/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Vim.G3d.Tests.Properties { // 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.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -61,7 +61,7 @@ internal Resources() { } /// - /// Looks up a localized string similar to C:\DEV\g3d\csharp\Vim.G3d.Tests\ + /// Looks up a localized string similar to C:\Users\Rober\Desktop\Vim\vim-format\src\cs\g3d\Vim.G3d.Tests\ ///. /// internal static string ProjDir { 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..ae2d33e9 100644 --- a/src/cs/g3d/Vim.G3d.Tests/Vim.G3d.Tests.csproj +++ b/src/cs/g3d/Vim.G3d.Tests/Vim.G3d.Tests.csproj @@ -14,6 +14,8 @@ + + @@ -34,4 +36,10 @@ + + + True + + + diff --git a/src/cs/g3d/Vim.G3d/G3D.cs b/src/cs/g3d/Vim.G3d/G3D.cs index 98aed23b..36e90ca7 100644 --- a/src/cs/g3d/Vim.G3d/G3D.cs +++ b/src/cs/g3d/Vim.G3d/G3D.cs @@ -7,11 +7,12 @@ 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; +using Vim.BFastLib; +using System.Diagnostics; namespace Vim.G3d { @@ -293,22 +294,52 @@ public Vector3 ComputeFaceNormal(int nFace) public static G3D Read(string filePath) { using (var stream = File.OpenRead(filePath)) - return stream.ReadG3d(); + { + var bfast = new BFast(stream); + return Read(bfast); + } } - public static G3D Read(Stream stream) - => stream.ReadG3d(); - public static G3D Read(byte[] bytes) + public static G3D Read(BFast bfast) { - using (var stream = new MemoryStream(bytes)) - return stream.ReadG3d(); + var header = G3dHeader.FromBytesOrDefault(bfast.GetArray("meta")); + var attributes = new List(); + foreach (var name in bfast.Entries) + { + if (name == "meta") continue; + var attribute = GetEmptyAttribute(name); + if (attribute == null) continue; + var a = attribute.Read(bfast); + attributes.Add(a); + } + + return new G3D(attributes, header); + } + private static GeometryAttribute GetEmptyAttribute(string name) + { + if (!AttributeDescriptor.TryParse(name, out var attributeDescriptor)) + { + Debug.WriteLine("G3D Error: Could not parse attribute " + name); + return null; + } + try + { + return attributeDescriptor.ToDefaultAttribute(0); + } + catch + { + Debug.WriteLine("G3D Error: Could not parse attribute " + name); + return null; + } } + 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/G3dSerialization.cs b/src/cs/g3d/Vim.G3d/G3dSerialization.cs index ba7e59a7..4f47b27d 100644 --- a/src/cs/g3d/Vim.G3d/G3dSerialization.cs +++ b/src/cs/g3d/Vim.G3d/G3dSerialization.cs @@ -1,7 +1,8 @@ using System; using System.IO; -using System.Linq; -using Vim.BFast; +using Vim.BFastLib; +using Vim.LinqArray; +using System.Collections.Generic; namespace Vim.G3d { @@ -17,107 +18,18 @@ public static void WriteAttribute(Stream stream, GeometryAttribute attribute, st 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) + public static BFast ToBFast(this IGeometryAttributes self, G3dHeader? header = null) { - 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 + var bfast = new BFast(); + bfast.SetArray("meta", (header ?? G3dHeader.Default).ToBytes()); + foreach(var attribute in self.Attributes.ToEnumerable()) { - // Eat the exception and return. - return ReadFailure(); + attribute.AddTo(bfast); } - - // Success; consume the stream. - geometryAttribute = defaultAttribute.Read(stream, size); - return true; + return bfast; } - 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/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 index 221a576e..9b2030f9 100644 --- a/src/cs/g3d/Vim.G3d/GeometryAttribute.cs +++ b/src/cs/g3d/Vim.G3d/GeometryAttribute.cs @@ -2,9 +2,10 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using Vim.BFast; using Vim.LinqArray; using Vim.Math3d; +using Vim.BFastLib; +using Vim.BFastLib.Core; namespace Vim.G3d { @@ -85,6 +86,13 @@ public GeometryAttribute AsType() where T : unmanaged /// public abstract GeometryAttribute Read(Stream stream, long byteCount); + /// + /// Loads the correct typed data from a BFastNext. + /// + public abstract GeometryAttribute Read(BFast bfast); + + public abstract void AddTo(BFast bfast); + /// /// Creates a new GeometryAttribute with the same data, but with a different index. Useful when constructing attributes /// @@ -220,6 +228,17 @@ public override GeometryAttribute Read(Stream stream, long byteCount) return new GeometryAttribute(data.ToIArray(), Descriptor); } + public override GeometryAttribute Read(BFast bfast) + { + var array = bfast.GetArray(Name); + return new GeometryAttribute(array.ToIArray(), Descriptor); + } + + public override void AddTo(BFast bfast) + { + bfast.SetArray(Name, Data.ToArray()); + } + public override GeometryAttribute SetIndex(int index) => index == Descriptor.Index ? this : new GeometryAttribute(Data, Descriptor.SetIndex(index)); } diff --git a/src/cs/g3d/Vim.G3d/Header.cs b/src/cs/g3d/Vim.G3d/Header.cs index c37140ce..60d4bb09 100644 --- a/src/cs/g3d/Vim.G3d/Header.cs +++ b/src/cs/g3d/Vim.G3d/Header.cs @@ -40,6 +40,18 @@ public static G3dHeader FromBytes(byte[] bytes) } .Validate(); + public static G3dHeader FromBytesOrDefault(byte[] bytes) + { + try + { + return FromBytes(bytes).Validate(); + } + catch (Exception) + { + return Default; + } + } + public static G3dHeader Default = new G3dHeader { diff --git a/src/cs/g3d/Vim.G3d/Vim.G3d.csproj b/src/cs/g3d/Vim.G3d/Vim.G3d.csproj index 7f4cdcb4..f3bb78c2 100644 --- a/src/cs/g3d/Vim.G3d/Vim.G3d.csproj +++ b/src/cs/g3d/Vim.G3d/Vim.G3d.csproj @@ -19,8 +19,9 @@ It can be easily and efficiently deserialized and rendered in different language - + True + @@ -48,9 +49,9 @@ It can be easily and efficiently deserialized and rendered in different language - - - + + + diff --git a/src/cs/g3d/Vim.G3dNext.CodeGen/CodeBuilder.cs b/src/cs/g3d/Vim.G3dNext.CodeGen/CodeBuilder.cs new file mode 100644 index 00000000..8dd3283a --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext.CodeGen/CodeBuilder.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Vim.G3dNext.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.G3dNext.CodeGen/Definitions.cs b/src/cs/g3d/Vim.G3dNext.CodeGen/Definitions.cs new file mode 100644 index 00000000..9eb47307 --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext.CodeGen/Definitions.cs @@ -0,0 +1,62 @@ +using Vim.Math3d; + +namespace Vim.G3dNext.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.G3dNext.CodeGen/G3dBuffer.cs b/src/cs/g3d/Vim.G3dNext.CodeGen/G3dBuffer.cs new file mode 100644 index 00000000..97cf4ce1 --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext.CodeGen/G3dBuffer.cs @@ -0,0 +1,48 @@ +using System; +using System.Diagnostics; + +namespace Vim.G3dNext.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.G3dNext.CodeGen/G3dCodeGen.cs b/src/cs/g3d/Vim.G3dNext.CodeGen/G3dCodeGen.cs new file mode 100644 index 00000000..5724b22f --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext.CodeGen/G3dCodeGen.cs @@ -0,0 +1,134 @@ +using System; +using System.IO; +using System.Linq; + +namespace Vim.G3dNext.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.G3dNext"); + 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 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 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.G3dNext.CodeGen/G3dEntity.cs b/src/cs/g3d/Vim.G3dNext.CodeGen/G3dEntity.cs new file mode 100644 index 00000000..2be5f9e7 --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext.CodeGen/G3dEntity.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; + +namespace Vim.G3dNext.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.G3dNext.CodeGen/Program.cs b/src/cs/g3d/Vim.G3dNext.CodeGen/Program.cs new file mode 100644 index 00000000..5470fb2f --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext.CodeGen/Program.cs @@ -0,0 +1,11 @@ +namespace Vim.G3dNext.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.G3dNext.CodeGen/Vim.G3dNext.CodeGen.csproj b/src/cs/g3d/Vim.G3dNext.CodeGen/Vim.G3dNext.CodeGen.csproj new file mode 100644 index 00000000..41239fb1 --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext.CodeGen/Vim.G3dNext.CodeGen.csproj @@ -0,0 +1,26 @@ + + + netstandard2.0;net6.0 + OnOutputUpdated + + + + Exe + + Vim.G3dNext.CodeGen.Program + + + + + + + + + + + + True + + + + \ No newline at end of file diff --git a/src/cs/g3d/Vim.G3dNext.Tests/Vim.G3dNext.Tests.csproj b/src/cs/g3d/Vim.G3dNext.Tests/Vim.G3dNext.Tests.csproj new file mode 100644 index 00000000..158c815b --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext.Tests/Vim.G3dNext.Tests.csproj @@ -0,0 +1,37 @@ + + + netstandard2.0 + false + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + diff --git a/src/cs/g3d/Vim.G3dNext/BufferMethods.cs b/src/cs/g3d/Vim.G3dNext/BufferMethods.cs new file mode 100644 index 00000000..e4f74d70 --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext/BufferMethods.cs @@ -0,0 +1,63 @@ +using System; +using System.IO; +using System.Linq; + +namespace Vim.G3dNext +{ + 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.G3dNext/Constants.cs b/src/cs/g3d/Vim.G3dNext/Constants.cs new file mode 100644 index 00000000..c8cc2282 --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext/Constants.cs @@ -0,0 +1,47 @@ +namespace Vim.G3dNext +{ + /// + /// 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 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 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.G3dNext/G3dChunk.cs b/src/cs/g3d/Vim.G3dNext/G3dChunk.cs new file mode 100644 index 00000000..8ad82bd3 --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext/G3dChunk.cs @@ -0,0 +1,164 @@ +using System; +using Vim.Math3d; + +namespace Vim.G3dNext +{ + 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.G3dNext/G3dGenerated.g.cs b/src/cs/g3d/Vim.G3dNext/G3dGenerated.g.cs new file mode 100644 index 00000000..5d2a7b8d --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext/G3dGenerated.g.cs @@ -0,0 +1,471 @@ +// AUTO-GENERATED FILE, DO NOT MODIFY. +// ReSharper disable All +using Vim.BFastLib; + +namespace Vim.G3dNext +{ + // 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 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 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 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 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 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 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 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 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.G3dNext/G3dMaterials.cs b/src/cs/g3d/Vim.G3dNext/G3dMaterials.cs new file mode 100644 index 00000000..18c3138a --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext/G3dMaterials.cs @@ -0,0 +1,18 @@ + +namespace Vim.G3dNext +{ + 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.G3dNext/G3dScene.cs b/src/cs/g3d/Vim.G3dNext/G3dScene.cs new file mode 100644 index 00000000..4d6adb03 --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext/G3dScene.cs @@ -0,0 +1,12 @@ +namespace Vim.G3dNext +{ + 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.G3dNext/G3dVim.cs b/src/cs/g3d/Vim.G3dNext/G3dVim.cs new file mode 100644 index 00000000..3ddb9885 --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext/G3dVim.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using Vim.BFastLib; + +namespace Vim.G3dNext +{ + 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; + } + + 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 int GetTriangleCount() + { + return GetIndexCount() / 3; + } + + /// + /// 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; + + /// + /// The total number of shape vertices. + /// + public int GetShapeVertexCount() => ShapeVertices?.Length ?? 0; + } +} diff --git a/src/cs/g3d/Vim.G3dNext/MetaHeader.cs b/src/cs/g3d/Vim.G3dNext/MetaHeader.cs new file mode 100644 index 00000000..a58a3a6f --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext/MetaHeader.cs @@ -0,0 +1,63 @@ +using System; +using System.Text; + +namespace Vim.G3dNext +{ + // 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.G3dNext/Vim.G3dNext.csproj b/src/cs/g3d/Vim.G3dNext/Vim.G3dNext.csproj new file mode 100644 index 00000000..b0086596 --- /dev/null +++ b/src/cs/g3d/Vim.G3dNext/Vim.G3dNext.csproj @@ -0,0 +1,22 @@ + + + netstandard2.0 + + + + + + + + + + + + + + True + + + + + diff --git a/src/cs/vim/Vim.Format.Core/AssetInfo.cs b/src/cs/vim/Vim.Format.Core/AssetInfo.cs index 46a75349..7f63be47 100644 --- a/src/cs/vim/Vim.Format.Core/AssetInfo.cs +++ b/src/cs/vim/Vim.Format.Core/AssetInfo.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.IO; -using Vim.BFast; +using Vim.BFastLib; using Vim.Util; using Vim.LinqArray; diff --git a/src/cs/vim/Vim.Format.Core/BigG3dWriter.cs b/src/cs/vim/Vim.Format.Core/BigG3dWriter.cs index ccdf6e40..61cb63e8 100644 --- a/src/cs/vim/Vim.Format.Core/BigG3dWriter.cs +++ b/src/cs/vim/Vim.Format.Core/BigG3dWriter.cs @@ -1,210 +1,210 @@ -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 - { - private INamedBuffer Meta { get; } - private string[] Names { get; } - private long[] Sizes { get; } - private BFastHeader Header { get; } - private List Meshes { get; } - private List Instances { get; } - private List Shapes { get; } - private List Materials { get; } - - // Computed fields - private int[] MeshVertexOffsets { get; } - private int[] MeshIndexOffsets { get; } - private int[] MeshSubmeshOffset { get; } - private int[] SubmeshIndexOffsets { get; } - private 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; - }); - } - } -} +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using Vim.G3dNext; +//using Vim.BFastLib; +//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 +// { +// private INamedBuffer Meta { get; } +// private string[] Names { get; } +// private long[] Sizes { get; } +// private BFastHeader Header { get; } +// private List Meshes { get; } +// private List Instances { get; } +// private List Shapes { get; } +// private List Materials { get; } + +// // Computed fields +// private int[] MeshVertexOffsets { get; } +// private int[] MeshIndexOffsets { get; } +// private int[] MeshSubmeshOffset { get; } +// private int[] SubmeshIndexOffsets { get; } +// private 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 8ae6b0e6..54806f6b 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using Vim.BFast; +using Vim.BFastLib; using Vim.LinqArray; namespace Vim.Format diff --git a/src/cs/vim/Vim.Format.Core/Document.cs b/src/cs/vim/Vim.Format.Core/Document.cs index 39801323..a86e2123 100644 --- a/src/cs/vim/Vim.Format.Core/Document.cs +++ b/src/cs/vim/Vim.Format.Core/Document.cs @@ -1,5 +1,5 @@ using Vim.LinqArray; -using Vim.BFast; +using Vim.BFastLib; namespace Vim.Format { diff --git a/src/cs/vim/Vim.Format.Core/DocumentBuilder.cs b/src/cs/vim/Vim.Format.Core/DocumentBuilder.cs index a1d54198..efd07c5e 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.G3d; namespace Vim.Format { @@ -14,11 +15,11 @@ 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 List Meshes = new List(); + //public readonly List Instances = new List(); + //public readonly List Shapes = new List(); + //public readonly List Materials = new List(); + public readonly G3dBuilder Geometry = new G3dBuilder(); public bool UseColors { get; set; } public DocumentBuilder( @@ -51,51 +52,64 @@ public DocumentBuilder AddAsset(string name, byte[] asset) return this; } - public DocumentBuilder AddMesh(SubdividedMesh g) + public DocumentBuilder AddMesh(SubdividedMesh 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 +148,17 @@ public List ComputeEntityTables(IReadOnlyDictionary ComputeEntityTables(IReadOnlyDictionary g.Vertices.Count)); - tb.AddDataColumn("int:FaceCount", Meshes.Select(g => g.Indices.Count / 3)); + tb.AddDataColumn("int:VertexCount", Geometry.GetVertexCounts()); + tb.AddDataColumn("int:FaceCount", Geometry.GetFaceCounts()); } // TODO: add bounding box information to the nodes @@ -180,13 +194,15 @@ public List ComputeEntityTables(IReadOnlyDictionary kv.Value.ToNamedBuffer(kv.Key)) as IEnumerable; Debug.Assert(assets != null, "Asset conversion to IEnumerable failed."); @@ -195,7 +211,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 3136b414..508eb5aa 100644 --- a/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Linq; -using Vim.BFast; +using Vim.BFastLib; using Vim.Format.Geometry; using Vim.G3d; using Vim.LinqArray; diff --git a/src/cs/vim/Vim.Format.Core/EntityTable.cs b/src/cs/vim/Vim.Format.Core/EntityTable.cs index f26d9ae4..21e1a72d 100644 --- a/src/cs/vim/Vim.Format.Core/EntityTable.cs +++ b/src/cs/vim/Vim.Format.Core/EntityTable.cs @@ -1,5 +1,5 @@ using System; -using Vim.BFast; +using Vim.BFastLib; using Vim.LinqArray; namespace Vim.Format diff --git a/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs b/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs index b985100b..91add434 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..3033844c --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/G3dBuilder.cs @@ -0,0 +1,133 @@ +using System.Collections.Generic; +using System.Linq; +using Vim.G3d; +using Vim.BFastLib; +using static Vim.Format.DocumentBuilder; +using Vim.Math3d; + +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(SubdividedMesh mesh) + { + _meshes.Add(mesh); + } + + public void AddMeshes(IEnumerable meshes) + { + foreach (var mesh in meshes) + { + AddMesh(mesh); + } + } + + public void AddInstance(Instance instance) + { + _instances.Add(instance); + } + + public void AddInstances(IEnumerable instances) + { + foreach (var instance in instances) + { + AddInstance(instance); + } + } + + public void AddShape(Shape shape) + { + _shapes.Add(shape); + } + + public void AddMaterial(Material 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 SubdividedMesh 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.Count).ToArray(); + } + + public int[] GetFaceCounts() + { + return _meshes.Select(m => m.Indices.Count / 3).ToArray(); + } + + + public BFast ToBFast() + { + var bfast = new BFast(); + var totalSubmeshCount = _meshes.Select(s => s.SubmeshesIndexOffset.Count).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.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; + } + + // 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/Validation.cs b/src/cs/vim/Vim.Format.Core/Geometry/Validation.cs new file mode 100644 index 00000000..abf23eef --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/Geometry/Validation.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using Vim.G3d; +using Vim.LinqArray; +using Vim.BFastLib; + +namespace Vim.Format.Geometry +{ + public static class Validation + { + public static void ValidateTableRows(this Document doc) + { + foreach (var et in doc.EntityTables.Values.ToArray()) + { + foreach (var c in et.IndexColumns.Values.ToArray()) + { + if (c.Array.Length != et.NumRows) + throw new Exception($"Expected array length {c.Array.Length} of column {c.Name} to be the same as number of rows {et.NumRows}"); + } + + foreach (var c in et.DataColumns.Values.ToArray()) + { + if (c.NumElements() != et.NumRows) + throw new Exception($"Expected array length {c.NumElements()} of column {c.Name} to be the same as number of rows {et.NumRows}"); + } + + foreach (var c in et.StringColumns.Values.ToArray()) + { + if (c.Array.Length != et.NumRows) + throw new Exception($"Expected array length {c.Array.Length} of column {c.Name} to be the same as number of rows {et.NumRows}"); + } + } + } + + public static void ValidateIndexColumns(this Document doc) + { + foreach (var et in doc.EntityTables.Values.ToArray()) + { + foreach (var ic in et.IndexColumns.Values.ToEnumerable()) + { + var table = ic.GetRelatedTable(doc); + if (table == null) + throw new Exception($"Could not find related table for index column {ic.Name}"); + } + } + } + + public static string[] RequiredAttributeNames => new [] + { + // Vertices + CommonAttributes.Position, + CommonAttributes.Index, + + // Meshes + CommonAttributes.MeshSubmeshOffset, + + // Submeshes + CommonAttributes.SubmeshIndexOffset, + + // Instances + CommonAttributes.InstanceMesh, + CommonAttributes.InstanceTransform, + }; + + public static void ValidateGeometryAttributes(this Document doc) + { + var attributes = doc.Geometry.Attributes; + var attributeNameSet = new HashSet(attributes.Select(a => a.Name).ToEnumerable()); + foreach (var attributeName in RequiredAttributeNames) + { + if (!attributeNameSet.Contains(attributeName)) + throw new Exception($"Required attribute {attributeName} was not found."); + } + } + + public static void ValidateAssets(this Document doc) + { + foreach (var asset in doc.Assets.Values.ToEnumerable()) + AssetInfo.Parse(asset.Name); // This will throw if it fails to parse. + } + + public static void Validate(this Document doc) + { + doc.ValidateTableRows(); + doc.ValidateIndexColumns(); + doc.ValidateGeometryAttributes(); + doc.ValidateAssets(); + } + + // TODO: ValidateShapes() to validate VIM files which contain optional 2d data (shapes/overlays). + + public static void ValidateIndices(this IMesh mesh) + { + foreach (var index in mesh.Indices.ToEnumerable()) + { + if (index < 0 || index >= mesh.NumVertices) + throw new Exception($"Invalid mesh index: {index}. Expected a value greater or equal to 0 and less than {mesh.NumVertices}"); + } + } + + public static void Validate(this IMesh mesh) + { + mesh.ValidateIndices(); + } + } +} diff --git a/src/cs/vim/Vim.Format.Core/SerializableDocument.cs b/src/cs/vim/Vim.Format.Core/SerializableDocument.cs index 6b834739..3a9ae487 100644 --- a/src/cs/vim/Vim.Format.Core/SerializableDocument.cs +++ b/src/cs/vim/Vim.Format.Core/SerializableDocument.cs @@ -1,44 +1,14 @@ using System; using System.Collections.Generic; +using System.IO; 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 /// @@ -88,5 +58,158 @@ public class SerializableDocument /// 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(Geometry != null) + { + bfast.SetBFast(BufferNames.Geometry, Geometry?.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.Geometry = G3D.Read(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 = ReadEntityTable(b, schemaOnly); + table.Name = entry; + yield return table; + } + } + + /// + /// Returns a SerializableEntityTable based on the given buffer reader. + /// + public static SerializableEntityTable ReadEntityTable( + 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; + } } } 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..dd724bb9 --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/SerializableEntityTable.cs @@ -0,0 +1,69 @@ +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); + + public static SerializableEntityTable FromBfast(string name, BFast bfast) + { + return null; + } + + 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 : ""; + } + + 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 59b06bc2..c2e9010b 100644 --- a/src/cs/vim/Vim.Format.Core/SerializableHeader.cs +++ b/src/cs/vim/Vim.Format.Core/SerializableHeader.cs @@ -1,30 +1,33 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; +using System.Text; +using Vim.BFastLib; using Vim.Util; namespace Vim.Format { public class SerializableHeader { - private static readonly SerializableVersion CurrentVimFormatVersion = VimFormatVersion.Current; - - private const string FormatVersionField = "vim"; - private const string IdField = "id"; - private const string RevisionField = "revision"; - private const string GeneratorField = "generator"; - private const string CreationDateField = "created"; - private const string SchemaField = "schema"; + public static readonly SerializableVersion CurrentVimFormatVersion = VimFormatVersion.Current; + + protected const string FormatVersionField = "vim"; + public const string IdField = "id"; + public const string RevisionField = "revision"; + public const string GeneratorField = "generator"; + public const string CreationDateField = "created"; + public const string SchemaField = "schema"; public const string BuildField = "build"; // optional - private const char Separator = '='; - private const char EndOfLineChar = '\n'; - private const string EndOfLineString = "\n"; - private const string PersistingIdSeparator = "::"; - private const string DummyPersistingIdPrefix = "unknown_"; + public const char Separator = '='; + public const char EndOfLineChar = '\n'; + public const string EndOfLineString = "\n"; + public const string PersistingIdSeparator = "::"; + public const string DummyPersistingIdPrefix = "unknown_"; - private static readonly string[] RequiredFields = + public static readonly string[] RequiredFields = { FormatVersionField, IdField, @@ -93,6 +96,30 @@ public SerializableHeader(string generator, SerializableVersion schema, string v AddOptionalValues(values ?? new Dictionary(), versionString)) { } + public static SerializableHeader FromBytes(byte[] input) + { + return FromString(Encoding.UTF8.GetString(input)); + } + + + /// + /// Returns the VIM header from a vim file stream. + /// Will throw if the file is not a valid VIM. + /// + public static SerializableHeader FromStream(Stream stream) + { + var bfast = new BFast(stream); + var bytes = bfast.GetArray(BufferNames.Header); + if (bytes == null) return null; + return SerializableHeader.FromBytes(bytes); + } + + public static SerializableHeader FromPath(string path) + { + using (var file = new FileStream(path, FileMode.OpenOrCreate)) + return FromStream(file); + } + /// /// Parses the input. Throws exceptions if the input does not define a correctly formatted header. /// @@ -100,7 +127,7 @@ public SerializableHeader(string generator, SerializableVersion schema, string v /// /// /// - public static SerializableHeader Parse(string input) + public static SerializableHeader FromString(string input) { var lines = input.Split(EndOfLineChar) .Where(str => !string.IsNullOrEmpty(str)); @@ -119,7 +146,7 @@ public static SerializableHeader Parse(string input) { var tokens = line.Split(Separator); var numTokens = tokens.Length; - + // skip empty lines. if (numTokens == 0) continue; @@ -252,6 +279,9 @@ public override bool Equals(object obj) public override int GetHashCode() => ToString().GetHashCode(); + public static string CreatePersistingId(Guid id, Guid revision) + => string.Join(PersistingIdSeparator, id.ToString(), revision.ToString()); + /// /// Used to generate an unknown persistence ID to avoid id collisions with other unknown references. /// @@ -269,6 +299,11 @@ public static string CreateDummyPersistingId() /// /// public string PersistingId - => string.Join(PersistingIdSeparator, Id.ToString(), Revision.ToString()); + => 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 index 402d124d..f89bb5f2 100644 --- a/src/cs/vim/Vim.Format.Core/Serializer.cs +++ b/src/cs/vim/Vim.Format.Core/Serializer.cs @@ -1,328 +1,250 @@ -using System.Collections.Generic; -using System.Linq; -using System; -using Vim.BFast; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; -using Vim.G3d; -using Vim.Util; - -namespace Vim.Format -{ - public static class Serializer - { - public static List ToBuffers(this SerializableEntityTable table) - { - var r = new List(); - - r.AddRange(table.DataColumns); - r.AddRange(table.IndexColumns); - r.AddRange(table.StringColumns); - - return r; - } - - public static readonly Regex TypePrefixRegex = new Regex(@"(\w+:).*"); - - public static string GetTypePrefix(this string name) - { - var match = TypePrefixRegex.Match(name); - return match.Success ? match.Groups[1].Value : ""; - } - - /// - /// Returns the named buffer prefix, or null if no prefix was found. - /// - public static string GetTypePrefix(this INamedBuffer namedBuffer) - => namedBuffer.Name.GetTypePrefix(); - - /// - /// Returns a NamedBuffer representing to an entity table column. - /// If schemaOnly is enabled, the column is returned without any of its contained data; - /// this is useful for rapidly querying the schema of the entity table. - /// - public static NamedBuffer ReadEntityTableColumn( - this BFastBufferReader columnBufferReader, - bool schemaOnly) where T : unmanaged - { - var (name, size) = columnBufferReader; - - if (schemaOnly) - return new Buffer(Array.Empty()).ToNamedBuffer(name); - - return columnBufferReader - .Seek() - .ReadBufferFromNumberOfBytes(size) - .ToNamedBuffer(name); - } - - /// - /// Returns a SerializableEntityTable based on the given buffer reader. - /// - public static SerializableEntityTable ReadEntityTable( - this BFastBufferReader entityTableBufferReader, - bool schemaOnly) - { - var et = new SerializableEntityTable { Name = entityTableBufferReader.Name }; - - foreach (var colBr in entityTableBufferReader.Seek().GetBFastBufferReaders()) - { - var name = colBr.Name; - var typePrefix = name.GetTypePrefix(); - - switch (typePrefix) - { - case VimConstants.IndexColumnNameTypePrefix: - { - et.IndexColumns.Add(colBr.ReadEntityTableColumn(schemaOnly)); - break; - } - case VimConstants.StringColumnNameTypePrefix: - { - et.StringColumns.Add(colBr.ReadEntityTableColumn(schemaOnly)); - break; - } - case VimConstants.IntColumnNameTypePrefix: - { - et.DataColumns.Add(colBr.ReadEntityTableColumn(schemaOnly)); - break; - } - case VimConstants.LongColumnNameTypePrefix: - { - et.DataColumns.Add(colBr.ReadEntityTableColumn(schemaOnly)); - break; - } - case VimConstants.DoubleColumnNameTypePrefix: - { - et.DataColumns.Add(colBr.ReadEntityTableColumn(schemaOnly)); - break; - } - case VimConstants.FloatColumnNameTypePrefix: - { - et.DataColumns.Add(colBr.ReadEntityTableColumn(schemaOnly)); - break; - } - case VimConstants.ByteColumnNameTypePrefix: - { - et.DataColumns.Add(colBr.ReadEntityTableColumn(schemaOnly)); - break; - } - // For flexibility, we ignore the columns which do not contain a recognized prefix. - } - } - - return et; - } - - /// - /// Enumerates the SerializableEntityTables contained in the given entities buffer. - /// - public static IEnumerable EnumerateEntityTables( - this BFastBufferReader entitiesBufferReader, - bool schemaOnly, - Func entityTableNameFilterFunc = null) - { - var entityTableBufferReaders = entitiesBufferReader.Seek() - .GetBFastBufferReaders(br => entityTableNameFilterFunc?.Invoke(br.Name) ?? true); +//using System.Collections.Generic; +//using System.Linq; +//using System; +//using Vim.BFastLib; +//using System.IO; +//using System.Text; +//using System.Text.RegularExpressions; +//using Vim.G3d; +//using Vim.Util; + +//namespace Vim.Format +//{ +// public static class Serializer +// { +// private static List ToBuffers(this SerializableEntityTable table) +// { +// var r = new List(); + +// r.AddRange(table.DataColumns); +// r.AddRange(table.IndexColumns); +// r.AddRange(table.StringColumns); + +// return r; +// } + +// private 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(); + +// /// +// /// Enumerates the SerializableEntityTables contained in the given entities buffer. +// /// +// public static IEnumerable EnumerateEntityTables( +// this BFastBufferReader entitiesBufferReader, +// bool schemaOnly, +// Func entityTableNameFilterFunc = null) +// { +// var entityTableBufferReaders = entitiesBufferReader.Seek() +// .GetBFastBufferReaders(br => entityTableNameFilterFunc?.Invoke(br.Name) ?? true); - foreach (var entityTableBufferReader in entityTableBufferReaders) - { - yield return entityTableBufferReader.ReadEntityTable(schemaOnly); - } - } - - /// - /// Enumerates the SerializableEntityTables contained in the given VIM file. - /// - public static IEnumerable EnumerateEntityTables( - this FileInfo vimFileInfo, - bool schemaOnly, - Func entityTableNameFilterFunc = null) - { - using (var stream = vimFileInfo.OpenRead()) - { - var entitiesBufferReader = stream.GetBFastBufferReader(BufferNames.Entities); - if (entitiesBufferReader == null) - yield break; - - foreach (var entityTable in entitiesBufferReader.EnumerateEntityTables(schemaOnly, entityTableNameFilterFunc)) - { - yield return entityTable; - } - } - } - - public static BFastBuilder ToBFastBuilder(this IEnumerable entityTables) - { - var bldr = new BFastBuilder(); - foreach (var et in entityTables) - { - bldr.Add(et.Name, et.ToBuffers()); - } - return bldr; - } - - public static BFastBuilder ToBFastBuilder(this SerializableDocument doc) - => CreateBFastBuilder( - doc.Header, - doc.Assets, - doc.StringTable, - doc.EntityTables, - doc.Geometry.ToG3DWriter()); - - public static BFastBuilder CreateBFastBuilder( - SerializableHeader header, - IEnumerable assets, - IEnumerable stringTable, - IEnumerable entityTables, - IBFastComponent geometry) - { - var bfastBuilder = new BFastBuilder(); - bfastBuilder.Add(BufferNames.Header, header.ToString().ToBytesUtf8().ToBuffer()); - bfastBuilder.Add(BufferNames.Assets, assets ?? Array.Empty()); - bfastBuilder.Add(BufferNames.Entities, entityTables.ToBFastBuilder()); - bfastBuilder.Add(BufferNames.Strings, stringTable.PackStrings().ToBuffer()); - bfastBuilder.Add(BufferNames.Geometry, geometry); - return bfastBuilder; - } - - public static void Serialize( - Stream stream, - SerializableHeader header, - IEnumerable assets, - IEnumerable stringTable, - IEnumerable entityTables, - IBFastComponent geometry) - { - CreateBFastBuilder(header, assets, stringTable, entityTables, geometry).Write(stream); - } - - public static void Serialize(this SerializableDocument doc, Stream stream) - => doc.ToBFastBuilder().Write(stream); - - public static void Serialize(this SerializableDocument document, string filePath) - { - using (var stream = File.OpenWrite(filePath)) - document.Serialize(stream); - } - - public static SerializableHeader ToSerializableHeader(this byte[] bytes) - => SerializableHeader.Parse(Encoding.UTF8.GetString(bytes)); - - /// - /// Returns true if the SerializableHeader in the stream is successfully parsed. - /// - public static bool TryParseSerializableHeader(this Stream stream, out SerializableHeader header) - { - using (new SeekContext(stream)) - { - try - { - header = stream.ReadBFastBuffer(BufferNames.Header)?.Array.ToSerializableHeader(); - } - catch - { - header = null; - } - return header != null; - } - } - - /// - /// Returns true if the SerializableHeader in the given VIM file is successfully parsed. - /// - public static bool TryParseSerializableHeader(this FileInfo fileInfo, out SerializableHeader header) - { - using (var fs = fileInfo.OpenRead()) - { - return fs.TryParseSerializableHeader(out header); - } - } - - /// - /// Returns the VIM file's header schema version. Returns null if the header schema is not found. - /// - public static string GetSchemaVersion(this FileInfo fileInfo) - => fileInfo.TryParseSerializableHeader(out var header) - ? header.Schema?.ToString() - : null; - - public static void ReadBuffer(this SerializableDocument doc, BFastBufferReader bufferReader) - { - var (name, numBytes) = bufferReader; - var stream = bufferReader.Seek(); - - switch (name) - { - case BufferNames.Header: - { - doc.Header = stream.ReadArray((int)numBytes).ToSerializableHeader(); - break; - } - - case BufferNames.Assets: - { - if (doc.Options?.SkipAssets == true) - break; - - doc.Assets = stream.ReadBFast().ToArray(); - break; - } - - case BufferNames.Strings: - { - doc.StringTable = ReadStrings(stream, numBytes); - break; - } - - case BufferNames.Geometry: - { - if (doc.Options?.SkipGeometry == true) - break; - - doc.Geometry = G3D.Read(stream); - break; - } - - case BufferNames.Entities: - { - doc.EntityTables = - bufferReader - .EnumerateEntityTables(doc.Options?.SchemaOnly ?? false) - .ToList(); - break; - } - } - } - - public static string[] ReadStrings(Stream stream, long numBytes) - { - var stringBytes = stream.ReadArray((int)numBytes); - var joinedStringTable = Encoding.UTF8.GetString(stringBytes); - return joinedStringTable.Split('\0'); - } - - public static SerializableDocument Deserialize(Stream stream, LoadOptions loadOptions = null) - { - var doc = new SerializableDocument { Options = loadOptions }; - - foreach (var buffer in stream.GetBFastBufferReaders()) - { - doc.ReadBuffer(buffer); - } - - return doc; - } - - public static SerializableDocument Deserialize(string filePath, LoadOptions loadOptions = null) - { - using (var stream = File.OpenRead(filePath)) - { - var doc = Deserialize(stream, loadOptions); - doc.FileName = filePath; - return doc; - } - } - } -} +// foreach (var entityTableBufferReader in entityTableBufferReaders) +// { +// yield return entityTableBufferReader.ReadEntityTable(schemaOnly); +// } +// } + +// /// +// /// Enumerates the SerializableEntityTables contained in the given VIM file. +// /// +// public static IEnumerable EnumerateEntityTables( +// this FileInfo vimFileInfo, +// bool schemaOnly, +// Func entityTableNameFilterFunc = null) +// { +// using (var stream = vimFileInfo.OpenRead()) +// { +// var entitiesBufferReader = stream.GetBFastBufferReader(BufferNames.Entities); +// if (entitiesBufferReader == null) +// yield break; + +// foreach (var entityTable in entitiesBufferReader.EnumerateEntityTables(schemaOnly, entityTableNameFilterFunc)) +// { +// yield return entityTable; +// } +// } +// } + +// private static BFastBuilder ToBFastBuilder(this IEnumerable entityTables) +// { +// var bldr = new BFastBuilder(); +// foreach (var et in entityTables) +// { +// bldr.Add(et.Name, et.ToBuffers()); +// } +// return bldr; +// } + +// private static BFastBuilder ToBFastBuilder(this SerializableDocument doc) +// => CreateBFastBuilder( +// doc.Header, +// doc.Assets, +// doc.StringTable, +// doc.EntityTables, +// doc.Geometry.ToG3DWriter()); + +// private 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.FileName = filePath; +// return doc; +// } +// } +// } +//} diff --git a/src/cs/vim/Vim.Format.Core/TableNameExtensions.cs b/src/cs/vim/Vim.Format.Core/TableNameExtensions.cs index 396cca53..be6c2072 100644 --- a/src/cs/vim/Vim.Format.Core/TableNameExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/TableNameExtensions.cs @@ -1,6 +1,6 @@ using System; using System.Text.RegularExpressions; -using Vim.BFast; +using Vim.BFastLib; namespace Vim.Format { diff --git a/src/cs/vim/Vim.Format.Core/TypePrefixExtensions.cs b/src/cs/vim/Vim.Format.Core/TypePrefixExtensions.cs new file mode 100644 index 00000000..43a991e8 --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/TypePrefixExtensions.cs @@ -0,0 +1,22 @@ +using System.Text.RegularExpressions; +using Vim.BFastLib; + +namespace Vim.Format +{ + public static class TypePrefixExtensions + { + private 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(); + } +} diff --git a/src/cs/vim/Vim.Format.Core/Validation.cs b/src/cs/vim/Vim.Format.Core/Validation.cs index 31026d99..3b6cda4b 100644 --- a/src/cs/vim/Vim.Format.Core/Validation.cs +++ b/src/cs/vim/Vim.Format.Core/Validation.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Vim.BFast; +using Vim.BFastLib; using Vim.G3d; using Vim.LinqArray; diff --git a/src/cs/vim/Vim.Format.Core/VimSchema.cs b/src/cs/vim/Vim.Format.Core/VimSchema.cs index f23d783a..42b8e208 100644 --- a/src/cs/vim/Vim.Format.Core/VimSchema.cs +++ b/src/cs/vim/Vim.Format.Core/VimSchema.cs @@ -45,7 +45,7 @@ public EntityTableSchema AddEntityTableSchema(string entityTableName) } public static VimSchema Create(string filePath) - => Create(new Document(Serializer.Deserialize(filePath))); + => Create(new Document(SerializableDocument.FromPath(filePath))); public static VimSchema Create(Document doc) { diff --git a/src/cs/vim/Vim.Format.Tests/FormatTests.cs b/src/cs/vim/Vim.Format.Tests/FormatTests.cs index 15b20f90..cb57dfa6 100644 --- a/src/cs/vim/Vim.Format.Tests/FormatTests.cs +++ b/src/cs/vim/Vim.Format.Tests/FormatTests.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using Vim.BFast; +using Vim.BFastLib; using Vim.LinqArray; namespace Vim.Format.Tests diff --git a/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs b/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs index 738effdd..14c153a6 100644 --- a/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs +++ b/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs @@ -298,8 +298,8 @@ public static void TriangleSerializationTest() .Add(submeshMaterials.ToIArray().ToSubmeshMaterialAttribute()) .ToG3D(); - var bfastBytes = g3d.WriteToBytes(); - var readG3d = G3D.Read(bfastBytes); + var bfast = g3d.ToBFast(); + var readG3d = G3D.Read(bfast); Assert.IsNotNull(readG3d); var mesh = readG3d.ToIMesh(); diff --git a/src/cs/vim/Vim.Format/Merge/MergeService.cs b/src/cs/vim/Vim.Format/Merge/MergeService.cs index eb6ac945..ce41cd29 100644 --- a/src/cs/vim/Vim.Format/Merge/MergeService.cs +++ b/src/cs/vim/Vim.Format/Merge/MergeService.cs @@ -4,7 +4,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Vim.BFast; +using Vim.BFastLib; using Vim.Format.Geometry; using Vim.LinqArray; using Vim.Math3d; @@ -120,7 +120,7 @@ public static DocumentBuilder MergeVimScenes( for (var i = 0; i < materialTable.NumRows; ++i) { - db.Materials.Add(ObjectModelStore.ConvertMaterialEntityFieldsToRenderableMaterial( + db.Geometry.AddMaterial(ObjectModelStore.ConvertMaterialEntityFieldsToRenderableMaterial( colorX: (float)colorXColumn.ElementAtOrDefault(i), colorY: (float)colorYColumn.ElementAtOrDefault(i), colorZ: (float)colorZColumn.ElementAtOrDefault(i), @@ -138,7 +138,7 @@ public static DocumentBuilder MergeVimScenes( var materialCounts = vims.Select(v => v.Materials.Count); var materialOffsets = materialCounts.ToIArray().PostAccumulate((x, y) => x + y).DropLast(); - db.Meshes.AddRange(vims + db.Geometry.AddMeshes(vims .SelectMany((vim, vimIndex) => vim.Meshes.Select(mesh => (mesh, vimIndex)).ToEnumerable()) .Select( pair => new DocumentBuilder.SubdividedMesh( @@ -170,7 +170,7 @@ public static DocumentBuilder MergeVimScenes( ct.ThrowIfCancellationRequested(); var allIdentity = vimTransforms.All(t => t.IsIdentity); - db.Instances.AddRange( + db.Geometry.AddInstances( vims .SelectMany((vim, vimIndex) => vim.VimNodes.Select(node => (node, vimIndex)).ToEnumerable()) .Select(pair => new DocumentBuilder.Instance() diff --git a/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs b/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs index a67fad64..b7b3af36 100644 --- a/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs +++ b/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs @@ -2,7 +2,7 @@ using System.Diagnostics; using System.Linq; using System.Threading; -using Vim.BFast; +using Vim.BFastLib; using Vim.LinqArray; using Vim.Util; @@ -16,9 +16,9 @@ internal class MergedTableBuilder public MergedTableBuilder(string name) => Name = name; - public Dictionary DataColumns = new Dictionary(); - public DictionaryOfLists IndexColumns = new DictionaryOfLists(); - public DictionaryOfLists StringColumns = new DictionaryOfLists(); + private readonly Dictionary DataColumns = new Dictionary(); + private readonly DictionaryOfLists IndexColumns = new DictionaryOfLists(); + private readonly DictionaryOfLists StringColumns = new DictionaryOfLists(); public void AddTable(EntityTable entityTable, Dictionary entityIndexOffsets) { diff --git a/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs b/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs index 78b0ae4f..8914d0f0 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs @@ -3,7 +3,7 @@ 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; @@ -19,12 +19,12 @@ public class VimValidationOptions public static class Validation { - public class VimValidationException : Exception + private class VimValidationException : Exception { public VimValidationException(string message) : base(message) { } } - public static void ValidateGeometry(this VimScene vim) + private static void ValidateGeometry(this VimScene vim) { // Validate the packed geometry. vim.Document.Geometry.ToIMesh().Validate(); @@ -34,7 +34,7 @@ public static void ValidateGeometry(this VimScene vim) g.Validate(); } - public static void ValidateDocumentModelToG3dInvariants(this VimScene vim) + private static void ValidateDocumentModelToG3dInvariants(this VimScene vim) { var g3d = vim._SerializableDocument.Geometry; var errors = new List(); @@ -94,13 +94,13 @@ public static void ValidateDocumentModelToG3dInvariants(this VimScene vim) } } - public static void ValidateNodes(this VimScene vim) + private static void ValidateNodes(this VimScene vim) { if (vim.VimNodes.Count != vim.DocumentModel.NumNode) throw new VimValidationException($"The number of {nameof(VimSceneNode)} ({vim.VimNodes.Count}) does not match the number of node entities ({vim.DocumentModel.NumNode})"); } - public static void ValidateShapes(this VimScene vim) + private static void ValidateShapes(this VimScene vim) { var shapes = vim.VimShapes; var numShapes = vim.DocumentModel.NumShape; @@ -153,12 +153,12 @@ public static void ValidateEquality(this DocumentBuilder db, VimScene vim) g.SubmeshMaterials.ToList() )).ToList(); - for (var i = 0; i < db.Meshes.Count; ++i) + for (var i = 0; i < db.Geometry.MeshCount; ++i) { - if (!db.Meshes[i].IsEquivalentTo(vimGeoBuilders[i])) + if (!db.Geometry.GetMesh(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])) + if (!db.Geometry.GetMesh(i).ToIMesh().GeometryEquals(vim.Meshes[i])) throw new VimValidationException($"{nameof(DocumentBuilder)} mesh {i} geometry is not equal to {nameof(VimScene)} mesh {i}"); } @@ -178,19 +178,5 @@ public static void ValidateEquality(this DocumentBuilder db, VimScene vim) // TODO: compare entity table equality. } } - - private 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}"); - } - } - - private static void Validate(this IMesh mesh) - { - mesh.ValidateIndices(); - } } } diff --git a/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs b/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs index 424cda93..039add74 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -10,8 +9,8 @@ using Vim.G3d; using Vim.LinqArray; using Vim.Math3d; - using IVimSceneProgress = System.IProgress<(string, double)>; +using Vim.BFastLib; namespace Vim { @@ -22,14 +21,38 @@ namespace Vim /// public class VimScene : IScene { + /// + /// 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); 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); @@ -165,6 +188,11 @@ private IStep[] GetInitSteps(bool inParallel) private void CreateMeshes(bool inParallel) { + if (_SerializableDocument.Geometry == null) + { + return; + } + var srcGeo = _SerializableDocument.Geometry; var tmp = srcGeo?.Meshes.Select(ToIMesh); Meshes = (tmp == null) @@ -176,18 +204,33 @@ private void CreateMeshes(bool inParallel) private void CreateShapes(bool inParallel) { + if (_SerializableDocument.Geometry == null) + { + return; + } + var r = _SerializableDocument.Geometry.Shapes.Select((s, i) => new VimShape(this, i)); VimShapes = inParallel ? r.EvaluateInParallel() : r.Evaluate(); } private void CreateScene(bool inParallel) { + if (_SerializableDocument.Geometry == null) + { + return; + } + VimNodes = CreateVimSceneNodes(this, _SerializableDocument.Geometry, inParallel); Nodes = VimNodes.Select(n => n as ISceneNode); } private void CreateMaterials(bool inParallel) { + if (_SerializableDocument.Geometry == null) + { + return; + } + var query = _SerializableDocument.Geometry.Materials.Select(m => new VimMaterial(m) as IMaterial); Materials = inParallel ? query.EvaluateInParallel() : query.Evaluate(); } @@ -203,7 +246,7 @@ public static IArray CreateVimSceneNodes(VimScene scene, G3D g3d, } public void Save(string filePath) - => _SerializableDocument.Serialize(filePath); + => _SerializableDocument.ToBFast().Write(filePath); public string FileName => _SerializableDocument.FileName; From a7e6ce9929e4d5bac7b4f95e7df2923dc48ccb16 Mon Sep 17 00:00:00 2001 From: Vadym Vorotilin Date: Wed, 4 Dec 2024 15:16:00 +0200 Subject: [PATCH 06/22] SerializableHeader extensions --- .../SerializableHeaderExtensions.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/cs/vim/Vim.Format.Core/SerializableHeaderExtensions.cs diff --git a/src/cs/vim/Vim.Format.Core/SerializableHeaderExtensions.cs b/src/cs/vim/Vim.Format.Core/SerializableHeaderExtensions.cs new file mode 100644 index 00000000..aea393b2 --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/SerializableHeaderExtensions.cs @@ -0,0 +1,41 @@ +using System.IO; + +namespace Vim.Format +{ + public static class SerializableHeaderExtensions + { + public static bool TryParseSerializableHeader(this Stream stream, out SerializableHeader header) + { + try + { + header = SerializableHeader.FromStream(stream); + } + catch + { + header = null; + } + return header != null; + } + + public static bool TryParseSerializableHeader(this FileInfo fileInfo, out SerializableHeader header) + { + try + { + header = SerializableHeader.FromPath(fileInfo.FullName); + } + catch + { + header = null; + } + return header != null; + } + + /// + /// Returns the VIM file's header schema version. Returns null if the header schema is not found. + /// + public static string GetSchemaVersion(this FileInfo fileInfo) + => fileInfo.TryParseSerializableHeader(out var header) + ? header.Schema?.ToString() + : null; + } +} From 3fbe4b5f08b8ad0b28ce9d403346363920e6d75d Mon Sep 17 00:00:00 2001 From: Vadym Vorotilin Date: Sat, 7 Dec 2024 15:49:05 +0200 Subject: [PATCH 07/22] Removing LinqArray where possible --- .../Vim.G3d.AssimpWrapper/AssimpExtensions.cs | 27 +- src/cs/g3d/Vim.G3d.Tests/G3dTests.cs | 7 +- src/cs/g3d/Vim.G3d/ArrayExtensions.cs | 81 ++ src/cs/g3d/Vim.G3d/AttributeExtensions.cs | 33 +- src/cs/g3d/Vim.G3d/CommonAttributes.cs | 242 ++--- src/cs/g3d/Vim.G3d/CommonAttributes.tt | 10 +- src/cs/g3d/Vim.G3d/G3D.cs | 102 +- src/cs/g3d/Vim.G3d/G3DBuilder.cs | 10 +- src/cs/g3d/Vim.G3d/G3dMesh.cs | 34 +- src/cs/g3d/Vim.G3d/G3dSerialization.cs | 4 +- src/cs/g3d/Vim.G3d/G3dShape.cs | 4 +- src/cs/g3d/Vim.G3d/GeometryAttribute.cs | 18 +- src/cs/g3d/Vim.G3d/GeometryAttributes.cs | 5 +- .../Vim.G3d/GeometryAttributesExtensions.cs | 86 +- src/cs/g3d/Vim.G3d/IGeometryAttributes.cs | 4 +- src/cs/g3d/Vim.G3d/ObjExporter.cs | 2 +- src/cs/g3d/Vim.G3d/Validation.cs | 2 +- src/cs/math3d/Vim.Math3D.Tests/Util.cs | 2 +- src/cs/samples/Vim.JsonDigest/AreaInfo.cs | 4 +- src/cs/samples/Vim.JsonDigest/MaterialInfo.cs | 2 +- src/cs/vim/Vim.Format.Core/AssetInfo.cs | 2 +- .../ColumnExtensions.Buffer.cs | 20 +- src/cs/vim/Vim.Format.Core/Document.cs | 17 +- .../DocumentBuilderExtensions.cs | 15 +- .../vim/Vim.Format.Core/DocumentExtensions.cs | 88 -- src/cs/vim/Vim.Format.Core/EntityTable.cs | 41 +- src/cs/vim/Vim.Format.Core/EntityTable_v2.cs | 10 +- src/cs/vim/Vim.Format.Core/Geometry/IMesh.cs | 20 +- src/cs/vim/Vim.Format.Core/Geometry/IScene.cs | 6 +- .../Geometry/MeshExtensions.cs | 25 +- .../Geometry/MeshOptimization.cs | 2 +- .../Vim.Format.Core/Geometry/Primitives.cs | 25 +- .../Geometry/SceneExtensions.cs | 9 +- .../Vim.Format.Core/Geometry/Validation.cs | 22 +- src/cs/vim/Vim.Format.Core/Validation.cs | 7 +- src/cs/vim/Vim.Format.Core/VimSchema.cs | 2 +- .../Vim.Format.Tests/EntityTable_v2_Tests.cs | 5 +- src/cs/vim/Vim.Format.Tests/FormatTests.cs | 21 +- .../Geometry/GeometryTests.cs | 20 +- src/cs/vim/Vim.Format/Merge/MergeService.cs | 25 +- .../Vim.Format/Merge/MergedTableBuilder.cs | 12 +- .../vim/Vim.Format/ObjectModel/ElementInfo.cs | 1 + .../ObjectModel/ObjectModelExtensions.cs | 4 +- .../ObjectModel/ObjectModelGenerated.cs | 914 +++++++++--------- .../vim/Vim.Format/ObjectModel/Validation.cs | 7 +- .../vim/Vim.Format/SceneBuilder/Validation.cs | 9 +- .../vim/Vim.Format/SceneBuilder/VimScene.cs | 34 +- .../SceneBuilder/VimSceneHelpers.cs | 3 +- .../vim/Vim.Format/SceneBuilder/VimShape.cs | 6 +- 49 files changed, 976 insertions(+), 1075 deletions(-) create mode 100644 src/cs/g3d/Vim.G3d/ArrayExtensions.cs delete mode 100644 src/cs/vim/Vim.Format.Core/DocumentExtensions.cs diff --git a/src/cs/g3d/Vim.G3d.AssimpWrapper/AssimpExtensions.cs b/src/cs/g3d/Vim.G3d.AssimpWrapper/AssimpExtensions.cs index 80e26239..3d025d24 100644 --- a/src/cs/g3d/Vim.G3d.AssimpWrapper/AssimpExtensions.cs +++ b/src/cs/g3d/Vim.G3d.AssimpWrapper/AssimpExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using Assimp; -using Vim.LinqArray; using Vim.Math3d; namespace Vim.G3d.AssimpWrapper @@ -32,17 +31,17 @@ public static bool IsTriangular(this Mesh mesh) 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 meshes = scene.Meshes.Select(m => m.ToG3D()).ToArray(); + var nodes = scene.GetNodes().ToArray(); + if (nodes.Length == 0 || nodes.Length == 1) + return meshes.Length > 0 ? meshes[0] : G3D.Empty; - var mergedAttributes = meshes.Merge().Attributes.ToList(); + var mergedAttributes = meshes.Select(m => (IGeometryAttributes) m).ToArray().Merge().Attributes.ToList(); - var subGeoTransforms = nodes.Select(n => n.Transform.ToMath3D()).ToInstanceTransformAttribute(); + var subGeoTransforms = nodes.Select(n => n.Transform.ToMath3D()).ToArray().ToInstanceTransformAttribute(); mergedAttributes.Add(subGeoTransforms); - var meshIndices = nodes.Select(n => n.MeshIndex).ToInstanceMeshAttribute(); + var meshIndices = nodes.Select(n => n.MeshIndex).ToArray().ToInstanceMeshAttribute(); mergedAttributes.Add(meshIndices); return mergedAttributes.ToG3d(); @@ -73,28 +72,28 @@ public static G3D ToG3D(this Mesh mesh) 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.AddVertices(mesh.Vertices.ToArray().Select(ToMath3D).ToArray()); bldr.AddIndices(indices); if (mesh.HasTangentBasis) - bldr.Add(mesh.BiTangents.ToIArray().Select(ToMath3D).ToVertexBitangentAttribute()); + bldr.Add(mesh.BiTangents.Select(ToMath3D).ToArray().ToVertexBitangentAttribute()); if (mesh.HasTangentBasis) - bldr.Add(mesh.Tangents.ToIArray().Select(x => ToMath3D(x).ToVector4()).ToVertexTangentAttribute()); + bldr.Add(mesh.Tangents.Select(x => ToMath3D(x).ToVector4()).ToArray().ToVertexTangentAttribute()); if (mesh.HasNormals) - bldr.Add(mesh.Normals.ToIArray().Select(ToMath3D).ToVertexNormalAttribute()); + bldr.Add(mesh.Normals.Select(ToMath3D).ToArray().ToVertexNormalAttribute()); for (var i = 0; i < mesh.TextureCoordinateChannelCount; ++i) { var uvChannel = mesh.TextureCoordinateChannels[i]; - bldr.Add(uvChannel.ToIArray().Select(ToMath3D).ToVertexUvwAttribute(i)); + bldr.Add(uvChannel.Select(ToMath3D).ToArray().ToVertexUvwAttribute(i)); } for (var i = 0; i < mesh.VertexColorChannelCount; ++i) { var vcChannel = mesh.VertexColorChannels[i]; - bldr.Add(vcChannel.ToIArray().Select(ToMath3D).ToVertexColorAttribute(i)); + bldr.Add(vcChannel.Select(ToMath3D).ToArray().ToVertexColorAttribute(i)); } return bldr.ToG3D(); diff --git a/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs b/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs index fa9f38ed..393a694a 100644 --- a/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs +++ b/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs @@ -6,7 +6,6 @@ using System.Linq; using Vim.BFastLib; using Vim.G3d.AssimpWrapper; -using Vim.LinqArray; using Vim.Math3d; namespace Vim.G3d.Tests @@ -218,9 +217,9 @@ public static void TriangleTest() var materialIndices = new[] { 5 }; var g3d = new G3DBuilder() - .AddVertices(vertices.ToIArray()) - .AddIndices(indices.ToIArray()) - .Add(materialIndices.ToIArray().ToFaceMaterialAttribute()) + .AddVertices(vertices) + .AddIndices(indices) + .Add(materialIndices.ToFaceMaterialAttribute()) .ToG3D(); var bfast = g3d.ToBFast(); diff --git a/src/cs/g3d/Vim.G3d/ArrayExtensions.cs b/src/cs/g3d/Vim.G3d/ArrayExtensions.cs new file mode 100644 index 00000000..3ac12173 --- /dev/null +++ b/src/cs/g3d/Vim.G3d/ArrayExtensions.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Vim.G3d +{ + public static class ArrayExtensions + { + public static IList Select(this int count, Func f) + { + var array = new T[count]; + + for (var i = 0; i < count; i++) + { + array[i] = f(i); + } + + return array; + } + + public static IList Repeat(this T value, int count) + { + var array = new T[count]; + + for (var i = 0; i < count; i++) + { + array[i] = value; + } + + return array; + } + + public static IList SelectByIndex(this IList self, IList indices) + => indices.Select(i => self[i]).ToArray(); + + public static IEnumerable Indices(this IList self) + => Enumerable.Range(0, self.Count).ToArray(); + + public static IList Slice(this IList self, int from, int to) + => Select(to - from, i => self[i + from]); + + public static IList SubArray(this IList self, int from, int count) + => self.Slice(from, count + from); + + public static IList Range(this int self) + => Select(self, i => i); + + /// + /// Creates a new array that concatenates a unit item list of one item before it + /// Repeatedly calling Prepend would result in significant performance degradation. + /// + public static IList Prepend(this IList self, T x) + => (self.Count + 1).Select(i => i == 0 ? x : self[i - 1]); + + public static T ElementAtOrDefault(this IList xs, int n, T defaultValue = default) + => xs != null && n >= 0 && n < xs.Count ? xs[n] : defaultValue; + + /// + /// Applies a function (like "+") to each element in the series to create an effect similar to partial sums. + /// The first value in the array will be zero. + /// + public static IList PostAccumulate(this IList self, Func f, T init = default) + { + var n = self.Count; + var r = new T[n + 1]; + var prev = r[0] = init; + if (n == 0) return r; + for (var i = 0; i < n; ++i) + { + prev = r[i + 1] = f(prev, self[i]); + } + return r; + } + + /// + /// Returns a new array containing all elements excluding the last n elements. + /// + public static IList DropLast(this IList self, int n = 1) + => self.Count > n ? self.Take(self.Count - n).ToArray() : Array.Empty(); + } +} diff --git a/src/cs/g3d/Vim.G3d/AttributeExtensions.cs b/src/cs/g3d/Vim.G3d/AttributeExtensions.cs index 98fb02fa..567bbd89 100644 --- a/src/cs/g3d/Vim.G3d/AttributeExtensions.cs +++ b/src/cs/g3d/Vim.G3d/AttributeExtensions.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using Vim.LinqArray; using Vim.Math3d; namespace Vim.G3d @@ -18,22 +17,16 @@ public static GeometryAttribute CheckAssociation(this GeometryAttribute 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 + public static GeometryAttribute ToAttribute(this IList 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 + public static GeometryAttribute ToAttribute(this IList self, string desc, int index) where T : unmanaged => self.ToAttribute(AttributeDescriptor.Parse(desc).SetIndex(index)); - public static IArray AttributeToColors(this GeometryAttribute attr) + public static IList AttributeToColors(this GeometryAttribute attr) { var desc = attr.Descriptor; if (desc.DataType == DataType.dt_float32) @@ -41,22 +34,22 @@ public static IArray AttributeToColors(this GeometryAttribute attr) if (desc.DataArity == 4) return attr.AsType().Data; if (desc.DataArity == 3) - return attr.AsType().Data.Select(vc => new Vector4(vc, 1f)); + return attr.AsType().Data.Select(vc => new Vector4(vc, 1f)).ToArray(); if (desc.DataArity == 2) - return attr.AsType().Data.Select(vc => new Vector4(vc.X, vc.Y, 0, 1f)); + return attr.AsType().Data.Select(vc => new Vector4(vc.X, vc.Y, 0, 1f)).ToArray(); if (desc.DataArity == 1) - return attr.AsType().Data.Select(vc => new Vector4(vc, vc, vc, 1f)); + return attr.AsType().Data.Select(vc => new Vector4(vc, vc, vc, 1f)).ToArray(); } 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)); + return attr.AsType().Data.Select(b => new Vector4(b.X / 255f, b.Y / 255f, b.Z / 255f, b.W / 255f)).ToArray(); if (desc.DataArity == 3) - return attr.AsType().Data.Select(b => new Vector4(b.X / 255f, b.Y / 255f, b.Z / 255f, 1f)); + return attr.AsType().Data.Select(b => new Vector4(b.X / 255f, b.Y / 255f, b.Z / 255f, 1f)).ToArray(); if (desc.DataArity == 2) - return attr.AsType().Data.Select(b => new Vector4(b.X / 255f, b.Y / 255f, 0f, 1f)); + return attr.AsType().Data.Select(b => new Vector4(b.X / 255f, b.Y / 255f, 0f, 1f)).ToArray(); if (desc.DataArity == 1) - return attr.AsType().Data.Select(b => new Vector4(b / 255f, b / 255f, b / 255f, 1f)); + return attr.AsType().Data.Select(b => new Vector4(b / 255f, b / 255f, b / 255f, 1f)).ToArray(); } Debug.WriteLine($"Failed to recongize color format {attr.Descriptor}"); return null; @@ -140,11 +133,5 @@ public static GeometryAttribute ToDefaultAttribute(this AttributeDescriptor 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/CommonAttributes.cs b/src/cs/g3d/Vim.G3d/CommonAttributes.cs index da835913..461c6e91 100644 --- a/src/cs/g3d/Vim.G3d/CommonAttributes.cs +++ b/src/cs/g3d/Vim.G3d/CommonAttributes.cs @@ -3,7 +3,7 @@ // This file is generated from CommonAttributeExtensions.tt -using Vim.LinqArray; +using System.Collections.Generic; using Vim.Math3d; namespace Vim.G3d @@ -46,186 +46,126 @@ public static class CommonAttributes 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 ToObjectFaceSizeAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.ObjectFaceSize, index); + public static GeometryAttribute ToObjectFaceSizeAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.ObjectFaceSize); 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 IList GetAttributeDataObjectFaceSize(this IGeometryAttributes self) => self.GetAttributeObjectFaceSize()?.Data; + public static GeometryAttribute ToIndexAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.Index, index); + public static GeometryAttribute ToIndexAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.Index); 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 IList GetAttributeDataIndex(this IGeometryAttributes self) => self.GetAttributeIndex()?.Data; + public static GeometryAttribute ToPositionAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.Position, index); + public static GeometryAttribute ToPositionAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.Position); 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 IList GetAttributeDataPosition(this IGeometryAttributes self) => self.GetAttributePosition()?.Data; + public static GeometryAttribute ToVertexUvAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexUv, index); + public static GeometryAttribute ToVertexUvAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexUv); 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 IList GetAttributeDataVertexUv(this IGeometryAttributes self) => self.GetAttributeVertexUv()?.Data; + public static GeometryAttribute ToVertexUvwAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexUvw, index); + public static GeometryAttribute ToVertexUvwAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexUvw); 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 IList GetAttributeDataVertexUvw(this IGeometryAttributes self) => self.GetAttributeVertexUvw()?.Data; + public static GeometryAttribute ToVertexNormalAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexNormal, index); + public static GeometryAttribute ToVertexNormalAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexNormal); 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 IList GetAttributeDataVertexNormal(this IGeometryAttributes self) => self.GetAttributeVertexNormal()?.Data; + public static GeometryAttribute ToVertexColorAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexColor, index); + public static GeometryAttribute ToVertexColorAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexColor); 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 IList GetAttributeDataVertexColor(this IGeometryAttributes self) => self.GetAttributeVertexColor()?.Data; + public static GeometryAttribute ToVertexColor8BitAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexColor8Bit, index); + public static GeometryAttribute ToVertexColor8BitAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexColor8Bit); 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 IList GetAttributeDataVertexColor8Bit(this IGeometryAttributes self) => self.GetAttributeVertexColor8Bit()?.Data; + public static GeometryAttribute ToVertexBitangentAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexBitangent, index); + public static GeometryAttribute ToVertexBitangentAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexBitangent); 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 IList GetAttributeDataVertexBitangent(this IGeometryAttributes self) => self.GetAttributeVertexBitangent()?.Data; + public static GeometryAttribute ToVertexTangentAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexTangent, index); + public static GeometryAttribute ToVertexTangentAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexTangent); 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 IList GetAttributeDataVertexTangent(this IGeometryAttributes self) => self.GetAttributeVertexTangent()?.Data; + public static GeometryAttribute ToVertexSelectionWeightAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexSelectionWeight, index); + public static GeometryAttribute ToVertexSelectionWeightAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexSelectionWeight); 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 IList GetAttributeDataVertexSelectionWeight(this IGeometryAttributes self) => self.GetAttributeVertexSelectionWeight()?.Data; + public static GeometryAttribute ToFaceColorAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.FaceColor, index); + public static GeometryAttribute ToFaceColorAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.FaceColor); 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 IList GetAttributeDataFaceColor(this IGeometryAttributes self) => self.GetAttributeFaceColor()?.Data; + public static GeometryAttribute ToFaceMaterialAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.FaceMaterial, index); + public static GeometryAttribute ToFaceMaterialAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.FaceMaterial); 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 IList GetAttributeDataFaceMaterial(this IGeometryAttributes self) => self.GetAttributeFaceMaterial()?.Data; + public static GeometryAttribute ToFaceNormalAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.FaceNormal, index); + public static GeometryAttribute ToFaceNormalAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.FaceNormal); 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 IList GetAttributeDataFaceNormal(this IGeometryAttributes self) => self.GetAttributeFaceNormal()?.Data; + public static GeometryAttribute ToMeshSubmeshOffsetAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.MeshSubmeshOffset, index); + public static GeometryAttribute ToMeshSubmeshOffsetAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.MeshSubmeshOffset); 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 IList GetAttributeDataMeshSubmeshOffset(this IGeometryAttributes self) => self.GetAttributeMeshSubmeshOffset()?.Data; + public static GeometryAttribute ToInstanceTransformAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.InstanceTransform, index); + public static GeometryAttribute ToInstanceTransformAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.InstanceTransform); 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 IList GetAttributeDataInstanceTransform(this IGeometryAttributes self) => self.GetAttributeInstanceTransform()?.Data; + public static GeometryAttribute ToInstanceParentAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.InstanceParent, index); + public static GeometryAttribute ToInstanceParentAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.InstanceParent); 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 IList GetAttributeDataInstanceParent(this IGeometryAttributes self) => self.GetAttributeInstanceParent()?.Data; + public static GeometryAttribute ToInstanceMeshAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.InstanceMesh, index); + public static GeometryAttribute ToInstanceMeshAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.InstanceMesh); 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 IList GetAttributeDataInstanceMesh(this IGeometryAttributes self) => self.GetAttributeInstanceMesh()?.Data; + public static GeometryAttribute ToInstanceFlagsAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.InstanceFlags, index); + public static GeometryAttribute ToInstanceFlagsAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.InstanceFlags); 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 IList GetAttributeDataInstanceFlags(this IGeometryAttributes self) => self.GetAttributeInstanceFlags()?.Data; + public static GeometryAttribute ToLineTangentInAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.LineTangentIn, index); + public static GeometryAttribute ToLineTangentInAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.LineTangentIn); 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 IList GetAttributeDataLineTangentIn(this IGeometryAttributes self) => self.GetAttributeLineTangentIn()?.Data; + public static GeometryAttribute ToLineTangentOutAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.LineTangentOut, index); + public static GeometryAttribute ToLineTangentOutAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.LineTangentOut); 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 IList GetAttributeDataLineTangentOut(this IGeometryAttributes self) => self.GetAttributeLineTangentOut()?.Data; + public static GeometryAttribute ToShapeVertexAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.ShapeVertex, index); + public static GeometryAttribute ToShapeVertexAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.ShapeVertex); 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 IList GetAttributeDataShapeVertex(this IGeometryAttributes self) => self.GetAttributeShapeVertex()?.Data; + public static GeometryAttribute ToShapeVertexOffsetAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.ShapeVertexOffset, index); + public static GeometryAttribute ToShapeVertexOffsetAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.ShapeVertexOffset); 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 IList GetAttributeDataShapeVertexOffset(this IGeometryAttributes self) => self.GetAttributeShapeVertexOffset()?.Data; + public static GeometryAttribute ToShapeColorAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.ShapeColor, index); + public static GeometryAttribute ToShapeColorAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.ShapeColor); 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 IList GetAttributeDataShapeColor(this IGeometryAttributes self) => self.GetAttributeShapeColor()?.Data; + public static GeometryAttribute ToShapeWidthAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.ShapeWidth, index); + public static GeometryAttribute ToShapeWidthAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.ShapeWidth); 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 IList GetAttributeDataShapeWidth(this IGeometryAttributes self) => self.GetAttributeShapeWidth()?.Data; + public static GeometryAttribute ToMaterialColorAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.MaterialColor, index); + public static GeometryAttribute ToMaterialColorAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.MaterialColor); 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 IList GetAttributeDataMaterialColor(this IGeometryAttributes self) => self.GetAttributeMaterialColor()?.Data; + public static GeometryAttribute ToMaterialGlossinessAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.MaterialGlossiness, index); + public static GeometryAttribute ToMaterialGlossinessAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.MaterialGlossiness); 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 IList GetAttributeDataMaterialGlossiness(this IGeometryAttributes self) => self.GetAttributeMaterialGlossiness()?.Data; + public static GeometryAttribute ToMaterialSmoothnessAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.MaterialSmoothness, index); + public static GeometryAttribute ToMaterialSmoothnessAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.MaterialSmoothness); 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 IList GetAttributeDataMaterialSmoothness(this IGeometryAttributes self) => self.GetAttributeMaterialSmoothness()?.Data; + public static GeometryAttribute ToSubmeshIndexOffsetAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.SubmeshIndexOffset, index); + public static GeometryAttribute ToSubmeshIndexOffsetAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.SubmeshIndexOffset); 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 IList GetAttributeDataSubmeshIndexOffset(this IGeometryAttributes self) => self.GetAttributeSubmeshIndexOffset()?.Data; + public static GeometryAttribute ToSubmeshMaterialAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.SubmeshMaterial, index); + public static GeometryAttribute ToSubmeshMaterialAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.SubmeshMaterial); public static GeometryAttribute GetAttributeSubmeshMaterial(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.SubmeshMaterial); - public static IArray GetAttributeDataSubmeshMaterial(this IGeometryAttributes self) => self.GetAttributeSubmeshMaterial()?.Data; + public static IList 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 index fad9970c..e73884ef 100644 --- a/src/cs/g3d/Vim.G3d/CommonAttributes.tt +++ b/src/cs/g3d/Vim.G3d/CommonAttributes.tt @@ -49,7 +49,7 @@ string[] namesDescriptorAndTypes = { }; #> -using Vim.LinqArray; +using System.Collections.Generic; using Vim.Math3d; namespace Vim.G3d @@ -81,12 +81,10 @@ namespace Vim.G3d 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 #>> To<#= name #>Attribute(this IList<<#= type #>> xs, int index) => xs.ToAttribute(<#= codeName #>, index); + public static GeometryAttribute<<#= type #>> To<#= name #>Attribute(this IList<<#= type #>> xs) => xs.ToAttribute(<#= codeName #>); 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; + public static IList<<#= type #>> GetAttributeData<#= name #>(this IGeometryAttributes self) => self.GetAttribute<#= name #>()?.Data; <# } #> diff --git a/src/cs/g3d/Vim.G3d/G3D.cs b/src/cs/g3d/Vim.G3d/G3D.cs index 36e90ca7..c4b97455 100644 --- a/src/cs/g3d/Vim.G3d/G3D.cs +++ b/src/cs/g3d/Vim.G3d/G3D.cs @@ -9,7 +9,6 @@ Usage licensed under terms of MIT License using System.Collections.Generic; using System.IO; using System.Linq; -using Vim.LinqArray; using Vim.Math3d; using Vim.BFastLib; using System.Diagnostics; @@ -31,64 +30,64 @@ public class G3D : GeometryAttributes // 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; } + public IList Vertices { get; } // Index buffer (one index per corner, and per half-edge). Computed if absent. - public IArray Indices { get; } + public IList 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; } + public List> AllVertexUvs { get; } = new List>(); + public List> AllVertexColors { get; } = new List>(); + public IList VertexUvs => AllVertexUvs?.ElementAtOrDefault(0); + public IList VertexColors => AllVertexColors?.ElementAtOrDefault(0); + public IList VertexNormals { get; } + public IList 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, + public IList FaceMaterials { get; } // Material indices per face, + public IList 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; } + public IList MeshIndexOffsets { get; } // Offset into the index buffer for each Mesh + public IList MeshVertexOffsets { get; } // Offset into the vertex buffer for each Mesh + public IList MeshIndexCounts { get; } // Computed + public IList MeshVertexCounts { get; } // Computed + public IList MeshSubmeshOffset { get; } + public IList MeshSubmeshCount { get; } // Computed + public IList 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. + public IList InstanceParents { get; } // Index of the parent transform + public IList InstanceTransforms { get; } // A 4x4 matrix in row-column order defining the transormed + public IList InstanceMeshes { get; } // The SubGeometry associated with the index + public IList 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 + public IList ShapeVertices { get; } + public IList ShapeVertexOffsets { get; } + public IList ShapeColors { get; } + public IList ShapeWidths { get; } + public IList ShapeVertexCounts { get; } // Computed + public IList Shapes { get; } // Computed // Materials - public IArray MaterialColors { get; } // RGBA with transparency. - public IArray MaterialGlossiness { get; } - public IArray MaterialSmoothness { get; } - public IArray Materials { get; } + public IList MaterialColors { get; } // RGBA with transparency. + public IList MaterialGlossiness { get; } + public IList MaterialSmoothness { get; } + public IList Materials { get; } // Submeshes - public IArray SubmeshIndexOffsets { get; } - public IArray SubmeshIndexCount { get; } - public IArray SubmeshMaterials { get; } + public IList SubmeshIndexOffsets { get; } + public IList SubmeshIndexCount { get; } + public IList 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()) + foreach (var attr in Attributes) { var desc = attr.Descriptor; switch (desc.Semantic) @@ -97,7 +96,7 @@ public G3D(IEnumerable attributes, G3dHeader? header = null, 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); + Indices = Indices ?? attr.AsType().Data.Select(x => (int)x).ToArray(); break; case Semantic.Position: @@ -111,14 +110,14 @@ public G3D(IEnumerable attributes, G3dHeader? header = null, case Semantic.Tangent: if (attr.IsTypeAndAssociation(Association.assoc_vertex)) - VertexTangents = VertexTangents ?? attr.AsType().Data.Select(v => v.ToVector4()); + VertexTangents = VertexTangents ?? attr.AsType().Data.Select(v => v.ToVector4()).ToArray(); 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())); + AllVertexUvs.Add(attr.AsType().Data.Select(uv => uv.ToVector2()).ToArray()); if (attr.IsTypeAndAssociation(Association.assoc_vertex)) AllVertexUvs.Add(attr.AsType().Data); break; @@ -208,7 +207,7 @@ public G3D(IEnumerable attributes, G3dHeader? header = null, // If no indices are provided then we are going to have to treat the index buffer as indices if (Indices == null) - Indices = Vertices.Indices(); + Indices = Vertices.Indices().ToArray(); // Compute face normals if possible if (FaceNormals == null && VertexNormals != null) @@ -219,8 +218,8 @@ public G3D(IEnumerable attributes, G3dHeader? header = null, // 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(); + MeshIndexOffsets = MeshSubmeshOffset.Select(submesh => SubmeshIndexOffsets[submesh]).ToArray(); + MeshSubmeshCount = GetSubArrayCounts(MeshSubmeshOffset.Count, MeshSubmeshOffset, NumSubmeshes); } if(MeshIndexOffsets != null) @@ -228,7 +227,8 @@ public G3D(IEnumerable attributes, G3dHeader? header = null, MeshIndexCounts = GetSubArrayCounts(NumMeshes, MeshIndexOffsets, NumCorners); MeshVertexOffsets = MeshIndexOffsets .Zip(MeshIndexCounts, (start, count) => (start, count)) - .Select(range => Indices.SubArray(range.start, range.count).Min()); + .Select(range => Indices.SubArray(range.start, range.count).Min()) + .ToArray(); } if (MeshVertexOffsets != null) @@ -236,11 +236,11 @@ public G3D(IEnumerable attributes, G3dHeader? header = null, } else { - MeshSubmeshCount = Array.Empty().ToIArray(); + MeshSubmeshCount = Array.Empty(); } if (SubmeshIndexOffsets != null) - SubmeshIndexCount = GetSubArrayCounts(SubmeshIndexOffsets.Count, SubmeshIndexOffsets, NumCorners).Evaluate(); + SubmeshIndexCount = GetSubArrayCounts(SubmeshIndexOffsets.Count, SubmeshIndexOffsets, NumCorners); // Compute all meshes Meshes = NumMeshes.Select(i => new G3dMesh(this, i)); @@ -253,13 +253,13 @@ public G3D(IEnumerable attributes, G3dHeader? header = null, ShapeVertices = Vector3.Zero.Repeat(0); if (ShapeVertexOffsets == null) - ShapeVertexOffsets = Array.Empty().ToIArray(); + ShapeVertexOffsets = Array.Empty(); if (ShapeColors == null) ShapeColors = Vector4.Zero.Repeat(0); if (ShapeWidths == null) - ShapeWidths = Array.Empty().ToIArray(); + ShapeWidths = Array.Empty(); // Update the instance options if (InstanceFlags == null) @@ -271,12 +271,12 @@ public G3D(IEnumerable attributes, G3dHeader? header = null, Shapes = NumShapes.Select(i => new G3dShape(this, i)); } - private static IArray GetSubArrayCounts(int numItems, IArray offsets, int totalCount) + private static IList GetSubArrayCounts(int numItems, IList 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) + private static void ValidateSubArrayCounts(IList subArrayCounts, string memberName) { for (var i = 0; i < subArrayCounts.Count; ++i) { @@ -285,10 +285,10 @@ private static void ValidateSubArrayCounts(IArray subArrayCounts, string me } } - public static Vector3 Average(IArray xs) + private static Vector3 Average(IList xs) => xs.Aggregate(Vector3.Zero, (a, b) => a + b) / xs.Count; - public Vector3 ComputeFaceNormal(int nFace) + private Vector3 ComputeFaceNormal(int nFace) => Average(NumCornersPerFace.Select(c => VertexNormals[nFace * NumCornersPerFace + c])); public static G3D Read(string filePath) diff --git a/src/cs/g3d/Vim.G3d/G3DBuilder.cs b/src/cs/g3d/Vim.G3d/G3DBuilder.cs index 6d68cb99..28c0a63a 100644 --- a/src/cs/g3d/Vim.G3d/G3DBuilder.cs +++ b/src/cs/g3d/Vim.G3d/G3DBuilder.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using Vim.LinqArray; using Vim.Math3d; namespace Vim.G3d @@ -20,16 +19,13 @@ public G3DBuilder Add(GeometryAttribute attr) return this; } - public G3DBuilder AddIndices(int[] indices) - => Add(indices.ToIndexAttribute()); - - public G3DBuilder AddIndices(IArray indices) + public G3DBuilder AddIndices(IList indices) => Add(indices.ToIndexAttribute()); public G3DBuilder SetObjectFaceSize(int objectFaceSize) - => Add(new[] { objectFaceSize }.ToIArray().ToObjectFaceSizeAttribute()); + => Add(new[] { objectFaceSize }.ToObjectFaceSizeAttribute()); - public G3DBuilder AddVertices(IArray vertices) + public G3DBuilder AddVertices(IList vertices) => Add(vertices.ToPositionAttribute()); public IGeometryAttributes ToIGeometryAttributes() diff --git a/src/cs/g3d/Vim.G3d/G3dMesh.cs b/src/cs/g3d/Vim.G3d/G3dMesh.cs index 689122c5..69ee18e4 100644 --- a/src/cs/g3d/Vim.G3d/G3dMesh.cs +++ b/src/cs/g3d/Vim.G3d/G3dMesh.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Vim.LinqArray; +using System.Linq; using Vim.Math3d; namespace Vim.G3d @@ -27,7 +27,7 @@ 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); + Indices = G3D.Indices?.SubArray(IndexOffset, NumCorners).Select(i => i - offset).ToArray(); VertexUvs = G3D.VertexUvs?.SubArray(VertexOffset, NumVertices); VertexNormals = G3D.VertexNormals?.SubArray(VertexOffset, NumVertices); VertexColors = G3D.VertexColors?.SubArray(VertexOffset, NumVertices); @@ -35,10 +35,10 @@ public G3dMesh(G3D parent, int index) 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 submeshArray = G3D.SubmeshIndexOffsets; + var submeshIndex = Array.BinarySearch(submeshArray.ToArray(), IndexOffset); var submeshCount = 0; - for(var i = submeshIndex; i < submeshArray.Length; i++) + for(var i = submeshIndex; i < submeshArray.Count; i++) { var indexOffset = submeshArray[i]; if (indexOffset - IndexOffset >= NumCorners) @@ -46,27 +46,27 @@ public G3dMesh(G3D parent, int index) submeshCount++; } SubmeshMaterials = G3D.SubmeshMaterials?.SubArray(submeshIndex, submeshCount); - SubmeshIndexOffsets = G3D.SubmeshIndexOffsets?.SubArray(submeshIndex, submeshCount).Select(i => i-IndexOffset); - MeshSubmeshOffset = new List() {0}.ToIArray(); + SubmeshIndexOffsets = G3D.SubmeshIndexOffsets?.SubArray(submeshIndex, submeshCount).Select(i => i-IndexOffset).ToArray(); + MeshSubmeshOffset = Array.Empty(); } // Vertex buffer. Usually present. - public IArray Vertices { get; } + public IList Vertices { get; } // Index buffer (one index per corner, and per half-edge) - public IArray Indices { get; } + public IList Indices { get; } // Vertex associated data - public IArray VertexUvs { get; } - public IArray VertexNormals { get; } - public IArray VertexColors { get; } - public IArray VertexTangents { get; } + public IList VertexUvs { get; } + public IList VertexNormals { get; } + public IList VertexColors { get; } + public IList VertexTangents { get; } // Face associated data. - public IArray FaceNormals { get; } + public IList FaceNormals { get; } - public IArray SubmeshMaterials { get; } - public IArray SubmeshIndexOffsets { get; } - public IArray MeshSubmeshOffset { get; } + public IList SubmeshMaterials { get; } + public IList SubmeshIndexOffsets { get; } + public IList MeshSubmeshOffset { get; } } } diff --git a/src/cs/g3d/Vim.G3d/G3dSerialization.cs b/src/cs/g3d/Vim.G3d/G3dSerialization.cs index 4f47b27d..7a17fc04 100644 --- a/src/cs/g3d/Vim.G3d/G3dSerialization.cs +++ b/src/cs/g3d/Vim.G3d/G3dSerialization.cs @@ -1,8 +1,6 @@ using System; using System.IO; using Vim.BFastLib; -using Vim.LinqArray; -using System.Collections.Generic; namespace Vim.G3d { @@ -22,7 +20,7 @@ public static BFast ToBFast(this IGeometryAttributes self, G3dHeader? header = n { var bfast = new BFast(); bfast.SetArray("meta", (header ?? G3dHeader.Default).ToBytes()); - foreach(var attribute in self.Attributes.ToEnumerable()) + foreach(var attribute in self.Attributes) { attribute.AddTo(bfast); } diff --git a/src/cs/g3d/Vim.G3d/G3dShape.cs b/src/cs/g3d/Vim.G3d/G3dShape.cs index 9cd220ad..d78a7faf 100644 --- a/src/cs/g3d/Vim.G3d/G3dShape.cs +++ b/src/cs/g3d/Vim.G3d/G3dShape.cs @@ -1,4 +1,4 @@ -using Vim.LinqArray; +using System.Collections.Generic; using Vim.Math3d; namespace Vim.G3d @@ -7,7 +7,7 @@ public class G3dShape { public readonly G3D G3D; public readonly int Index; - public readonly IArray Vertices; + public readonly IList Vertices; public int ShapeVertexOffset => G3D.ShapeVertexOffsets[Index]; public int NumVertices => G3D.ShapeVertexCounts[Index]; diff --git a/src/cs/g3d/Vim.G3d/GeometryAttribute.cs b/src/cs/g3d/Vim.G3d/GeometryAttribute.cs index 9b2030f9..7a09be78 100644 --- a/src/cs/g3d/Vim.G3d/GeometryAttribute.cs +++ b/src/cs/g3d/Vim.G3d/GeometryAttribute.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using Vim.LinqArray; using Vim.Math3d; using Vim.BFastLib; using Vim.BFastLib.Core; @@ -56,7 +55,7 @@ protected GeometryAttribute(AttributeDescriptor descriptor, int count) /// /// A mesh attribute can be remapped, using the given indices. /// - public abstract GeometryAttribute Remap(IArray indices); + public abstract GeometryAttribute Remap(IList indices); /// /// Converted to an INamedBuffer which consists of a name and an array of unmanaged types. @@ -106,9 +105,9 @@ public GeometryAttribute AsType() where T : unmanaged /// public class GeometryAttribute : GeometryAttribute where T : unmanaged { - public IArray Data; + public IList Data; - public GeometryAttribute(IArray data, AttributeDescriptor descriptor) + public GeometryAttribute(IList data, AttributeDescriptor descriptor) : base(descriptor, data.Count) { Data = data; @@ -205,13 +204,12 @@ public override GeometryAttribute Merge(IEnumerable others) return others .Select(ma => ma as GeometryAttribute) .Prepend(this) - .ToIArray() - .Select(attr => attr.Data) - .Flatten() + .SelectMany(attr => attr.Data) + .ToArray() .ToAttribute(Descriptor); } - public override GeometryAttribute Remap(IArray indices) + public override GeometryAttribute Remap(IList indices) => Data.SelectByIndex(indices).ToAttribute(Descriptor); public override INamedBuffer ToBuffer() @@ -225,13 +223,13 @@ public override GeometryAttribute Read(Stream stream, long byteCount) 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); + return new GeometryAttribute(data, Descriptor); } public override GeometryAttribute Read(BFast bfast) { var array = bfast.GetArray(Name); - return new GeometryAttribute(array.ToIArray(), Descriptor); + return new GeometryAttribute(array, Descriptor); } public override void AddTo(BFast bfast) diff --git a/src/cs/g3d/Vim.G3d/GeometryAttributes.cs b/src/cs/g3d/Vim.G3d/GeometryAttributes.cs index ac799c07..fd83c81d 100644 --- a/src/cs/g3d/Vim.G3d/GeometryAttributes.cs +++ b/src/cs/g3d/Vim.G3d/GeometryAttributes.cs @@ -8,7 +8,6 @@ Usage licensed under terms of MIT License using System; using System.Collections.Generic; using System.Linq; -using Vim.LinqArray; namespace Vim.G3d { @@ -30,7 +29,7 @@ public class GeometryAttributes : IGeometryAttributes public int NumShapeVertices { get; } = -1; public int NumShapes { get; } = -1; - public IArray Attributes { get; } + public IList Attributes { get; } public GeometryAttribute GetAttribute(string name) => Lookup.TryGetValue(name, out var val) ? val : null; @@ -119,7 +118,7 @@ public GeometryAttributes(IEnumerable attributes, int numCorn } // Now we create the public ordered list of attributes - Attributes = Lookup.Values.OrderBy(attr => attr.Name).ToIArray(); + Attributes = Lookup.Values.OrderBy(attr => attr.Name).ToArray(); // If the number of corner and faces are observed, one has to be a multiple of the other diff --git a/src/cs/g3d/Vim.G3d/GeometryAttributesExtensions.cs b/src/cs/g3d/Vim.G3d/GeometryAttributesExtensions.cs index ba7c1cfa..d7c5d85c 100644 --- a/src/cs/g3d/Vim.G3d/GeometryAttributesExtensions.cs +++ b/src/cs/g3d/Vim.G3d/GeometryAttributesExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Vim.LinqArray; using Vim.Math3d; namespace Vim.G3d @@ -36,8 +35,8 @@ public static int ExpectedElementCount(this IGeometryAttributes self, AttributeD return -1; } - public static IArray AttributeNames(this IGeometryAttributes g) - => g.Attributes.Select(attr => attr.Name); + public static string[] AttributeNames(this IGeometryAttributes g) + => g.Attributes.Select(attr => attr.Name).ToArray(); public static GeometryAttribute GetAttribute(this IGeometryAttributes g, string attributeName) where T : unmanaged => g.GetAttribute(attributeName)?.AsType(); @@ -93,20 +92,20 @@ public static int FaceToCorner(this IGeometryAttributes g, int f) /// /// Given a set of face indices, creates an array of corner indices /// - public static IArray FaceIndicesToCornerIndices(this IGeometryAttributes g, IArray faceIndices) + public static IList FaceIndicesToCornerIndices(this IGeometryAttributes g, IList 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[] FaceIndicesToFirstCornerIndices(this IGeometryAttributes g, IList faceIndices) + => faceIndices.Select(f => f * g.NumCornersPerFace).ToArray(); public static int CornerToFace(this IGeometryAttributes g, int c) => c / g.NumCornersPerFace; - public static IArray CornersToFaces(this IGeometryAttributes g) + public static IList CornersToFaces(this IGeometryAttributes g) => g.NumCorners.Select(g.CornerToFace); public static int CornerNumber(this IGeometryAttributes g, int c) @@ -115,22 +114,16 @@ public static int CornerNumber(this IGeometryAttributes g, int c) 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(); + => attributes.Attributes.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(); + => gs.Prepend(self).Merge(); - public static IGeometryAttributes Merge(this IArray geometryAttributesArray) + public static IGeometryAttributes Merge(this IList geometryAttributesArray) { if (geometryAttributesArray.Count == 0) return GeometryAttributes.Empty; @@ -156,7 +149,7 @@ public static IGeometryAttributes Merge(this IArray geometr .ToArray(); // Merge the non-indexed attributes - var others = geometryAttributesArray.Skip(1).ToEnumerable(); + var others = geometryAttributesArray.Skip(1); var attributeList = attributes.Select( attr => attr.Merge(others.Select(g => g.GetAttributeOrDefault(attr.Name)))).ToList(); @@ -194,7 +187,7 @@ public static IGeometryAttributes Merge(this IArray geometr /// representing the merged and offset values. /// public static int[] MergeIndexedAttribute( - this IArray geometryAttributesArray, + this IList geometryAttributesArray, Func> getIndexedAttributeFunc, Func getValueOffsetFunc, int initialValueOffset = 0) @@ -236,7 +229,7 @@ public static int[] MergeIndexedAttribute( /// Merges the attributes based on the given transformations and returns an array of merged values. /// public static T[] MergeAttributes( - this IArray geometryAttributesArray, + this IList geometryAttributesArray, Func> getAttributeFunc, Func<(IGeometryAttributes Parent, GeometryAttribute Attribute)[], T[]> mergeFunc) where T : unmanaged { @@ -259,8 +252,8 @@ public static T[] MergeAttributes( 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.Descriptor.Semantic == Semantic.Position && a is GeometryAttribute p) ? p.Data.Select(positionTransform).ToArray().ToAttribute(a.Descriptor) : + (a.Descriptor.Semantic == Semantic.Normal && a is GeometryAttribute n) ? n.Data.Select(normalTransform).ToArray().ToAttribute(a.Descriptor) : a) .ToGeometryAttributes(); @@ -270,7 +263,7 @@ 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.Descriptor.Semantic == Semantic.Position && a is GeometryAttribute p) ? p.Data.Select(positionTransform).ToArray().ToAttribute(a.Descriptor) : a) .ToGeometryAttributes(); @@ -280,13 +273,13 @@ public static IGeometryAttributes Deform(this IGeometryAttributes g, Func g.Deform(v => v.Transform(matrix), v => v.TransformNormal(matrix)); - public static IGeometryAttributes SetPosition(this IGeometryAttributes g, IArray points) + public static IGeometryAttributes SetPosition(this IGeometryAttributes g, Vector3[] 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 + public static IGeometryAttributes SetAttribute(this IGeometryAttributes self, ValueT[] values, AttributeDescriptor desc) where ValueT : unmanaged => self.SetAttribute(values.ToAttribute(desc)); /// @@ -294,7 +287,7 @@ public static IGeometryAttributes SetAttribute(this IGeometryAttributes /// 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) + public static IGeometryAttributes RemapFaces(this IGeometryAttributes g, IList faceRemap) => g.RemapFacesAndCorners(faceRemap, g.FaceIndicesToCornerIndices(faceRemap)); public static IEnumerable SetFaceSizeAttribute(this IEnumerable attributes, int numCornersPerFaces) @@ -309,7 +302,7 @@ public static IEnumerable SetFaceSizeAttribute(this IEnumerab /// 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) + public static IGeometryAttributes RemapFacesAndCorners(this IGeometryAttributes g, IList faceRemap, IList cornerRemap, int numCornersPerFace = -1) => g.VertexAttributes() .Concat(g.NoneAttributes()) .Concat(g.FaceAttributes().Select(attr => attr.Remap(faceRemap))) @@ -342,17 +335,17 @@ public static IGeometryAttributes TriangulateQuadMesh(this IGeometryAttributes g faceRemap[i * 2 + 1] = i; } - return g.RemapFacesAndCorners(faceRemap.ToIArray(), cornerRemap.ToIArray(), 3); + return g.RemapFacesAndCorners(faceRemap, cornerRemap, 3); } - public static IGeometryAttributes CopyFaces(this IGeometryAttributes g, IArray keep) + public static IGeometryAttributes CopyFaces(this IGeometryAttributes g, bool[] keep) => g.CopyFaces(i => keep[i]); - public static IGeometryAttributes CopyFaces(this IGeometryAttributes g, IArray keep) + public static IGeometryAttributes CopyFaces(this IGeometryAttributes g, int[] keep) => g.RemapFaces(keep); public static IGeometryAttributes CopyFaces(this IGeometryAttributes self, Func predicate) - => self.RemapFaces(self.NumFaces.Select(i => i).IndicesWhere(predicate).ToIArray()); + => self.RemapFaces(self.NumFaces.Select(i => i).Indices().Where(predicate).ToArray()); public static IGeometryAttributes DeleteFaces(this IGeometryAttributes g, Func predicate) => g.CopyFaces(i => !predicate(i)); @@ -364,7 +357,7 @@ public static IGeometryAttributes CopyFaces(this IGeometryAttributes g, int from /// 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) + public static IGeometryAttributes RemapVertices(this IGeometryAttributes g, int[] newVertices, int[] newIndices) => (new[] { newIndices.ToIndexAttribute() } .Concat( g.VertexAttributes() @@ -384,17 +377,17 @@ public static IGeometryAttributes RemapVertices(this IGeometryAttributes g, IArr /// 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) + public static IGeometryAttributes RemapVertices(this IGeometryAttributes g, int[] vertRemap) { var vertLookup = (-1).Repeat(g.NumVertices).ToArray(); - for (var i = 0; i < vertRemap.Count; ++i) + for (var i = 0; i < vertRemap.Length; ++i) { var oldVert = vertRemap[i]; vertLookup[oldVert] = i; } var oldIndices = g.GetAttributeIndex()?.Data ?? g.NumVertices.Range(); - var newIndices = oldIndices.Select(i => vertLookup[i]).Evaluate(); + var newIndices = oldIndices.Select(i => vertLookup[i]).ToArray(); if (newIndices.Any(x => x == -1)) throw new Exception("At least one of the indices references a vertex that no longer exists"); @@ -406,21 +399,21 @@ public static IGeometryAttributes RemapVertices(this IGeometryAttributes g, IArr /// 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) + public static IGeometryAttributes SelectFaces(this IGeometryAttributes g, int[] faces) { // Early exit, if all selected no need to do anything - if (g.NumFaces == faces.Count) + if (g.NumFaces == faces.Length) return g; // Early exit, if none selected no need to do anything - if (faces.Count == 0) + if (faces.Length == 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 oldSelIndices = new int[faces.Length * g.NumCornersPerFace]; // var oldIndices = faces.SelectMany(f => f.Indices()); - for (var i = 0; i < faces.Count; i++) + for (var i = 0; i < faces.Length; i++) { for (var t = 0; t < 3; t++) oldSelIndices[i * 3 + t] = oldIndices.Data[faces[i] * g.NumCornersPerFace + t]; @@ -455,7 +448,7 @@ public static IGeometryAttributes SelectFaces(this IGeometryAttributes g, IArray } //var faceRemapping = faces.Select(f => f.Index); - var vertRemapping = usedVertices.Take(numUsedVertices).ToIArray(); + var vertRemapping = usedVertices.Take(numUsedVertices).ToArray(); var cornerRemapping = g.FaceIndicesToCornerIndices(faces); return g.VertexAttributes() @@ -472,10 +465,7 @@ public static IGeometryAttributes SelectFaces(this IGeometryAttributes g, IArray 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) + public static IList 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) @@ -483,7 +473,7 @@ public static bool IsNormalAttribute(this GeometryAttribute attr) public static IEnumerable FlipNormalAttributes(this IEnumerable self) => self.Select(attr => attr.IsNormalAttribute() - ? attr.AsType().Data.Select(v => v.Inverse()).ToAttribute(attr.Descriptor) + ? attr.AsType().Data.Select(v => v.Inverse()).ToArray().ToAttribute(attr.Descriptor) : attr); public static IGeometryAttributes FlipWindingOrder(this IGeometryAttributes g) @@ -499,13 +489,13 @@ public static IGeometryAttributes FlipWindingOrder(this IGeometryAttributes g) public static IGeometryAttributes DoubleSided(this IGeometryAttributes g) => g.Merge(g.FlipWindingOrder()); - public static IArray DefaultMaterials(this IGeometryAttributes self) + public static IList DefaultMaterials(this IGeometryAttributes self) => (-1).Repeat(self.NumFaces); - public static IArray DefaultColors(this IGeometryAttributes self) + public static IList DefaultColors(this IGeometryAttributes self) => Vector4.Zero.Repeat(self.NumVertices); - public static IArray DefaultUvs(this IGeometryAttributes self) + public static IList DefaultUvs(this IGeometryAttributes self) => Vector2.Zero.Repeat(self.NumVertices); public static IGeometryAttributes Replace(this IGeometryAttributes self, Func selector, GeometryAttribute attribute) diff --git a/src/cs/g3d/Vim.G3d/IGeometryAttributes.cs b/src/cs/g3d/Vim.G3d/IGeometryAttributes.cs index e25da638..b83abf02 100644 --- a/src/cs/g3d/Vim.G3d/IGeometryAttributes.cs +++ b/src/cs/g3d/Vim.G3d/IGeometryAttributes.cs @@ -1,4 +1,4 @@ -using Vim.LinqArray; +using System.Collections.Generic; namespace Vim.G3d { @@ -16,7 +16,7 @@ public interface IGeometryAttributes int NumShapeVertices { get; } int NumShapes { get; } - IArray Attributes { get; } + IList Attributes { get; } GeometryAttribute GetAttribute(string name); } } diff --git a/src/cs/g3d/Vim.G3d/ObjExporter.cs b/src/cs/g3d/Vim.G3d/ObjExporter.cs index b76184dd..bd01456c 100644 --- a/src/cs/g3d/Vim.G3d/ObjExporter.cs +++ b/src/cs/g3d/Vim.G3d/ObjExporter.cs @@ -15,7 +15,7 @@ public static IEnumerable ObjLines(G3D g3d) // Write the vertices var vertices = g3d.Vertices; var uvs = g3d.VertexUvs; - foreach (var v in vertices.ToEnumerable()) + foreach (var v in vertices) yield return ($"v {v.X} {v.Y} {v.Z}"); if (uvs != null) { diff --git a/src/cs/g3d/Vim.G3d/Validation.cs b/src/cs/g3d/Vim.G3d/Validation.cs index b1fde949..1d7bfd8c 100644 --- a/src/cs/g3d/Vim.G3d/Validation.cs +++ b/src/cs/g3d/Vim.G3d/Validation.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using Vim.LinqArray; +using System.Linq; namespace Vim.G3d { diff --git a/src/cs/math3d/Vim.Math3D.Tests/Util.cs b/src/cs/math3d/Vim.Math3D.Tests/Util.cs index f5b5b705..9878d302 100644 --- a/src/cs/math3d/Vim.Math3D.Tests/Util.cs +++ b/src/cs/math3d/Vim.Math3D.Tests/Util.cs @@ -99,7 +99,7 @@ public static T Abs(T value) where T : struct return value; } - dynamic dyn = (dynamic)value; + var dyn = (dynamic)value; var abs = Math.Abs(dyn); var ret = (T)abs; return ret; diff --git a/src/cs/samples/Vim.JsonDigest/AreaInfo.cs b/src/cs/samples/Vim.JsonDigest/AreaInfo.cs index 017f2c04..73e5f840 100644 --- a/src/cs/samples/Vim.JsonDigest/AreaInfo.cs +++ b/src/cs/samples/Vim.JsonDigest/AreaInfo.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; using System.Collections.Generic; -using Vim.LinqArray; +using System.Linq; namespace Vim.JsonDigest { @@ -71,6 +71,6 @@ public static IEnumerable GetAreaInfoCollection(VimScene vimScene) Area = a.Value, Perimeter = a.Perimeter, IsGrossInterior = a.IsGrossInterior - }).ToEnumerable(); + }); } } diff --git a/src/cs/samples/Vim.JsonDigest/MaterialInfo.cs b/src/cs/samples/Vim.JsonDigest/MaterialInfo.cs index 898d2e0a..f4efdf86 100644 --- a/src/cs/samples/Vim.JsonDigest/MaterialInfo.cs +++ b/src/cs/samples/Vim.JsonDigest/MaterialInfo.cs @@ -70,7 +70,7 @@ public static IEnumerable GetMaterialInfoCollection(VimScene vimSc }).ToArray(); // Next, we iterate over all the MaterialInElement associative objects and update the MaterialInfos we created above. - foreach (var materialInElement in vimScene.DocumentModel.MaterialInElementList.ToEnumerable()) + foreach (var materialInElement in vimScene.DocumentModel.MaterialInElementList) { var material = materialInElement.Material; var element = materialInElement.Element; diff --git a/src/cs/vim/Vim.Format.Core/AssetInfo.cs b/src/cs/vim/Vim.Format.Core/AssetInfo.cs index 7f63be47..f5312e23 100644 --- a/src/cs/vim/Vim.Format.Core/AssetInfo.cs +++ b/src/cs/vim/Vim.Format.Core/AssetInfo.cs @@ -110,7 +110,7 @@ private static FileInfo ExtractAsset(this INamedBuffer assetBuffer, DirectoryInf 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/ColumnExtensions.Buffer.cs b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs index 54806f6b..e2008830 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs @@ -3,7 +3,7 @@ using System.Diagnostics; using System.Linq; using Vim.BFastLib; -using Vim.LinqArray; +using Vim.G3d; namespace Vim.Format { @@ -12,11 +12,12 @@ public static partial class ColumnExtensions private static IEnumerable GetAllColumns(this SerializableEntityTable et) => et.DataColumns.Concat(et.IndexColumns).Concat(et.StringColumns); - public static INamedBuffer[] ValidateColumnRowsAreAligned(this INamedBuffer[] columns) + public static IList ValidateColumnRowsAreAligned(this IEnumerable columns) { - var numRows = columns.FirstOrDefault()?.NumElements() ?? 0; + var result = columns.ToArray(); + var numRows = result.FirstOrDefault()?.NumElements() ?? 0; - foreach (var column in columns) + foreach (var column in result) { var columnRows = column.NumElements(); if (columnRows == numRows) @@ -26,10 +27,10 @@ public static INamedBuffer[] ValidateColumnRowsAreAligned(this INamedBuffer[] co Debug.Fail(msg); } - return columns; + return result; } - public static INamedBuffer[] ValidateColumnRowsAreAligned(this SerializableEntityTable et) + public static IList ValidateColumnRowsAreAligned(this SerializableEntityTable et) => et.GetAllColumns().ValidateColumnRowsAreAligned(); private static string ValidateCanConcatBuffers(this INamedBuffer thisBuffer, INamedBuffer otherBuffer) @@ -180,7 +181,6 @@ private static List> ConcatIntColumns(this IReadOnlyList thisColumnList.ConcatColumns(otherColumnList, (a, b) => new NamedBuffer(a.GetTypedData().Concat(b.GetTypedData()).ToArray(), a.Name)); -<<<<<<< HEAD /// /// Returns a concatenated SerializableEntityTable based on the column names of thisTable. /// @@ -199,12 +199,8 @@ public static SerializableEntityTable Concat( return concatenated; } - public static T[] GetColumnValues(this INamedBuffer nb) where T : unmanaged + public static IList GetColumnValues(this INamedBuffer nb) where T : unmanaged => nb.AsArray(); -======= - public static IArray GetColumnValues(this INamedBuffer nb) where T : unmanaged - => nb.AsArray().ToIArray(); ->>>>>>> ae310f2 (Refacroting API: public->private + cleanup unused) /// /// Returns a new collection of index columns in which the designated column names have repeated values of VimConstants.NoEntityRelation. diff --git a/src/cs/vim/Vim.Format.Core/Document.cs b/src/cs/vim/Vim.Format.Core/Document.cs index a86e2123..df4673d8 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 System.Collections.Generic; +using System.Linq; using Vim.BFastLib; +using Vim.G3d; +using Vim.Util; namespace Vim.Format { @@ -11,18 +14,18 @@ public Document(SerializableDocument document) _Document = document; Header = _Document.Header; Geometry = _Document.Geometry; - StringTable = _Document.StringTable.ToIArray(); - EntityTables = _Document.EntityTables.ToLookup( + StringTable = _Document.StringTable; + EntityTables = _Document.EntityTables.ToDictionary( et => et.Name, et => new EntityTable(this, et)); - Assets = _Document.Assets.ToLookup(et => et.Name, et => et); + Assets = _Document.Assets.ToDictionary(et => et.Name, et => et); } private SerializableDocument _Document { get; } public SerializableHeader Header { get; } - public ILookup EntityTables { get; } - public ILookup Assets { get; } - public IArray StringTable { get; } + public IDictionary EntityTables { get; } + public IDictionary Assets { get; } + public IList StringTable { get; } public string GetString(int index) => StringTable.ElementAtOrDefault(index); public G3d.G3D Geometry { get; } diff --git a/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs b/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs index 508eb5aa..aa9c046b 100644 --- a/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs @@ -3,7 +3,6 @@ using Vim.BFastLib; using Vim.Format.Geometry; using Vim.G3d; -using Vim.LinqArray; using static Vim.Format.DocumentBuilder; namespace Vim.Format @@ -11,9 +10,9 @@ 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()); + => gb.Vertices.ToArray().TriMesh( + gb.Indices.ToArray(), + submeshMaterials: gb.SubmeshMaterials.ToArray()); public static Material ToDocumentBuilderMaterial(this G3dMaterial g3dMaterial) => new Material @@ -35,17 +34,17 @@ private static void CreateTableCopy(this DocumentBuilder db, EntityTable table, 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)); @@ -54,7 +53,7 @@ private static void CreateTableCopy(this DocumentBuilder db, EntityTable table, public static void 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/DocumentExtensions.cs b/src/cs/vim/Vim.Format.Core/DocumentExtensions.cs deleted file mode 100644 index 53950875..00000000 --- a/src/cs/vim/Vim.Format.Core/DocumentExtensions.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Linq; -using System.Text.RegularExpressions; -using Vim.LinqArray; -using Vim.BFast; - -namespace Vim.Format -{ - public static class DocumentExtensions - { - public static Document ToDocument(this SerializableDocument document) - => new Document(document); - - public static EntityTable ToEntityTable(this SerializableEntityTable entityTable, Document document) - => new EntityTable(document, entityTable); - - public static string[] GetColumnNames(this EntityTable table) - => table.Columns.Select(b => b.Name).ToArray(); - - public static void ValidateRelations(this Document doc) - { - foreach (var et in doc.EntityTables.Values.ToEnumerable()) - { - foreach (var ic in et.IndexColumns.Values.ToEnumerable()) - { - var relatedTable = ic.GetRelatedTable(doc); - var maxValue = relatedTable.NumRows; - var data = ic.GetTypedData(); - for (var i = 0; i < data.Length; ++i) - { - var v = data[i]; - if (v < -1 || v > maxValue) - { - throw new Exception($"Invalid relation {v} out of range of -1 to {maxValue}"); - } - } - } - } - } - - public static readonly Regex IndexColumnNameComponentsRegex = new Regex(@"(\w+:)((?:\w|\.)+):(.+)"); - - public class IndexColumnNameComponents - { - public readonly string TypePrefix; - public readonly string TableName; - public readonly string FieldName; - - public IndexColumnNameComponents(string indexColumnName) - { - var match = IndexColumnNameComponentsRegex.Match(indexColumnName); - if (!match.Success) - throw new Exception($"Index column name {indexColumnName} could not be separated into its components."); - - TypePrefix = match.Groups[1].Value; - TableName = match.Groups[2].Value; - FieldName = match.Groups[3].Value; - } - } - - public static IndexColumnNameComponents SplitIndexColumnName(string name) - => new IndexColumnNameComponents(name); - - public static string GetRelatedTableNameFromColumnName(string name) - => SplitIndexColumnName(name).TableName; - - public static string GetFieldNameFromColumnName(string name) - => SplitIndexColumnName(name).FieldName; - - public static string GetFieldName(this INamedBuffer ic) - => GetFieldNameFromColumnName(ic.Name); - - public static string GetRelatedTableName(this INamedBuffer ic) - => GetRelatedTableNameFromColumnName(ic.Name); - - public static EntityTable GetRelatedTable(this INamedBuffer ic, Document doc) - => doc.GetTable(ic.GetRelatedTableName()); - - public static EntityTable GetTable(this Document doc, string name) - => doc.EntityTables.GetOrDefault(name); - - public static SerializableDocument SetFileName(this SerializableDocument doc, string fileName) - { - doc.FileName = fileName; - return doc; - } - } -} diff --git a/src/cs/vim/Vim.Format.Core/EntityTable.cs b/src/cs/vim/Vim.Format.Core/EntityTable.cs index 21e1a72d..2236e83b 100644 --- a/src/cs/vim/Vim.Format.Core/EntityTable.cs +++ b/src/cs/vim/Vim.Format.Core/EntityTable.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; +using System.Linq; using Vim.BFastLib; -using Vim.LinqArray; +using Vim.Util; namespace Vim.Format { @@ -12,34 +14,37 @@ public EntityTable(Document document, SerializableEntityTable entityTable) _EntityTable = entityTable; Name = _EntityTable.Name; - DataColumns = _EntityTable.DataColumns.ToLookup(c => c.Name, c => c); - IndexColumns = _EntityTable.IndexColumns.ToLookup(c => c.Name, c => c); - StringColumns = _EntityTable.StringColumns.ToLookup(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.ValidateColumnRowsAreAligned(); } private SerializableEntityTable _EntityTable { get; } public Document Document { get; } public string Name { get; } public int NumRows { get; } - public ILookup DataColumns { get; } - public ILookup> StringColumns { get; } - public ILookup> IndexColumns { get; } - public IArray Columns + public IDictionary DataColumns { get; } + public IDictionary> StringColumns { get; } + public IDictionary> IndexColumns { get; } + public IList 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)) + .ToArray(); - public IArray GetIndexColumnValues(string columnName) - => IndexColumns.GetOrDefault(columnName)?.GetColumnValues().ToIArray(); + public IList GetIndexColumnValues(string columnName) + => IndexColumns.GetOrDefault(columnName)?.GetColumnValues(); - public IArray GetStringColumnValues(string columnName) + public IList GetStringColumnValues(string columnName) => StringColumns.GetOrDefault(columnName) ?.GetColumnValues() ?.Select(Document.GetString) - .ToIArray(); + .ToArray(); - public IArray GetDataColumnValues(string columnName) where T : unmanaged + public IList GetDataColumnValues(string columnName) where T : unmanaged { var type = typeof(T); @@ -51,12 +56,12 @@ public IArray GetDataColumnValues(string columnName) where T : unmanaged return null; if (type == typeof(short)) - return namedBuffer.GetColumnValues().Select(i => (short)i).ToIArray() as IArray; + return namedBuffer.GetColumnValues().Select(i => (short)i) as IList; if (type == typeof(bool)) - return namedBuffer.GetColumnValues().Select(b => b != 0).ToIArray() as IArray; + return namedBuffer.GetColumnValues().Select(b => b != 0) as IList; - return namedBuffer.GetColumnValues().ToIArray(); + return namedBuffer.GetColumnValues(); } } } diff --git a/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs b/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs index 775e2190..a004c152 100644 --- a/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs +++ b/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Vim.BFast; +using Vim.BFastLib; using Vim.Util; namespace Vim.Format @@ -37,7 +37,7 @@ public EntityTable_v2( string[] stringBuffer) { Name = et.Name; - Columns = et.ValidateColumnRowsAreAligned(); + Columns = et.ValidateColumnRowsAreAligned().ToArray(); RowCount = Columns.FirstOrDefault()?.NumElements() ?? 0; foreach (var column in et.IndexColumns) @@ -58,7 +58,7 @@ private static T GetColumnOrDefault(Dictionary map, string key, T /// /// Returns the index column based on the given column name. /// - public int[] GetIndexColumnValues(string columnName) + public IList GetIndexColumnValues(string columnName) => GetColumnOrDefault(IndexColumns, columnName)?.GetColumnValues(); /// @@ -69,7 +69,7 @@ public string[] GetStringColumnValues(string columnName) var stringIndices = GetColumnOrDefault(StringColumns, columnName) ?.GetColumnValues() ?? Array.Empty(); - var strings = new string[stringIndices.Length]; + var strings = new string[stringIndices.Count]; for (var i = 0; i < strings.Length; i++) { @@ -84,7 +84,7 @@ public string[] GetStringColumnValues(string columnName) /// /// Returns the data column based on the given column name. /// - public T[] GetDataColumnValues(string columnName) where T : unmanaged + public IList GetDataColumnValues(string columnName) where T : unmanaged { var type = typeof(T); diff --git a/src/cs/vim/Vim.Format.Core/Geometry/IMesh.cs b/src/cs/vim/Vim.Format.Core/Geometry/IMesh.cs index f330d0e4..a861f08a 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/IMesh.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/IMesh.cs @@ -1,5 +1,5 @@ -using Vim.G3d; -using Vim.LinqArray; +using System.Collections.Generic; +using Vim.G3d; using Vim.Math3d; namespace Vim.Format.Geometry @@ -11,14 +11,14 @@ public interface IMesh : IGeometryAttributes, ITransformable3D { - IArray Vertices { get; } - IArray Indices { get; } - IArray VertexColors { get; } - IArray VertexNormals { get; } - IArray VertexUvs { get; } + IList Vertices { get; } + IList Indices { get; } + IList VertexColors { get; } + IList VertexNormals { get; } + IList VertexUvs { get; } - IArray SubmeshMaterials { get; } - IArray SubmeshIndexOffsets { get; } - IArray SubmeshIndexCount { get; } + IList SubmeshMaterials { get; } + IList SubmeshIndexOffsets { get; } + IList SubmeshIndexCount { get; } } } diff --git a/src/cs/vim/Vim.Format.Core/Geometry/IScene.cs b/src/cs/vim/Vim.Format.Core/Geometry/IScene.cs index 3f7acff6..c15e2032 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/IScene.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/IScene.cs @@ -1,4 +1,4 @@ -using Vim.LinqArray; +using System.Collections.Generic; using Vim.Math3d; namespace Vim.Format.Geometry @@ -8,8 +8,8 @@ namespace Vim.Format.Geometry /// public interface IScene { - IArray Nodes { get; } - IArray Meshes { get; } + IList Nodes { get; } + IList Meshes { get; } } /// diff --git a/src/cs/vim/Vim.Format.Core/Geometry/MeshExtensions.cs b/src/cs/vim/Vim.Format.Core/Geometry/MeshExtensions.cs index a6e3ac6e..9c37c614 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/MeshExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/MeshExtensions.cs @@ -2,25 +2,21 @@ using System.Collections.Generic; using System.Linq; using Vim.G3d; -using Vim.LinqArray; using Vim.Math3d; namespace Vim.Format.Geometry { public static class MeshExtensions { - 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()); + return new TriMesh(tmp.Attributes); case 4: - return new QuadMesh(tmp.Attributes.ToEnumerable()).ToTriMesh(); + return new QuadMesh(tmp.Attributes).ToTriMesh(); default: throw new Exception($"Can not convert a geometry with {tmp.NumCornersPerFace} to a triangle mesh: only quad meshes"); } @@ -29,7 +25,7 @@ public static IMesh ToIMesh(this IEnumerable self) public static IMesh ToIMesh(this IGeometryAttributes g) => g is IMesh m ? m : g is QuadMesh q ? q.ToIMesh() : g.Attributes.ToIMesh(); - public static IArray GetFaceMaterials(this IMesh mesh) + public static IList GetFaceMaterials(this IMesh mesh) { // SubmeshIndexOffsets: [0, A, B] // SubmeshIndexCount: [X, Y, Z] @@ -38,16 +34,15 @@ public static IArray GetFaceMaterials(this IMesh mesh) // 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(); + .ToArray(); } - public static IMesh Merge(this IArray meshes) - => meshes.Select(m => (IGeometryAttributes)m).Merge().ToIMesh(); + public static IMesh Merge(this IList meshes) + => meshes.Select(m => (IGeometryAttributes)m).ToArray().Merge().ToIMesh(); public static IMesh Merge(this IEnumerable meshes) - => meshes.ToIArray().Merge(); + => meshes.Merge(); public static IEnumerable<(int Material, IMesh Mesh)> SplitByMaterial(this IMesh mesh) { @@ -151,11 +146,11 @@ public static IGeometryAttributes ReverseWindingOrder(this IMesh mesh) r[i + 1] = mesh.Indices[i + 1]; r[i + 2] = mesh.Indices[i + 0]; } - return mesh.SetAttribute(r.ToIArray().ToIndexAttribute()); + return mesh.SetAttribute(r.ToIndexAttribute()); } public static AABox BoundingBox(this IMesh mesh) - => AABox.Create(mesh.Vertices.ToEnumerable()); + => AABox.Create(mesh.Vertices); public static Vector3 Center(this IMesh mesh) => mesh.BoundingBox().Center; @@ -169,7 +164,7 @@ public static Triangle VertexIndicesToTriangle(this IMesh mesh, Int3 indices) public static Triangle Triangle(this IMesh mesh, int face) => mesh.VertexIndicesToTriangle(mesh.FaceVertexIndices(face)); - public static IArray Triangles(this IMesh mesh) + public static IList Triangles(this IMesh mesh) => mesh.NumFaces.Select(mesh.Triangle); } } diff --git a/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs b/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs index 000bd2e3..e8944164 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs @@ -33,7 +33,7 @@ public MeshHash(IMesh mesh, float tolerance) 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); diff --git a/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs b/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs index 65d772a1..6479490a 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Linq; using Vim.G3d; -using Vim.LinqArray; using Vim.Math3d; namespace Vim.Format.Geometry @@ -15,12 +14,12 @@ 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) + this IList vertices, + IList indices = null, + IList uvs = null, + IList colors = null, + IList materials = null, + IList submeshMaterials = null) => TriMesh( vertices?.ToPositionAttribute(), indices?.ToIndexAttribute(), @@ -30,6 +29,12 @@ public static IMesh TriMesh( submeshMaterials?.ToSubmeshMaterialAttribute() ); + public static IMesh TriMesh(this IList vertices, IList indices = null, params GeometryAttribute[] attributes) + => new GeometryAttribute[] { + vertices?.ToPositionAttribute(), + indices?.ToIndexAttribute(), + }.Concat(attributes).ToIMesh(); + public static IMesh Cube { get @@ -45,7 +50,7 @@ public static IMesh Cube 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 @@ -66,7 +71,7 @@ public static IMesh Cube // top 3, 2, 6, 6, 7, 3 - }.ToIArray(); + }; return vertices.TriMesh(indices); } @@ -77,7 +82,7 @@ public static IMesh CubeFaceted get { var cube = Cube; - return cube.Indices.Select(i => cube.Vertices[i]).TriMesh(cube.Indices.Count.Range()); + return cube.Indices.Select(i => cube.Vertices[i]).ToArray().TriMesh(cube.Indices.Count.Range()); } } diff --git a/src/cs/vim/Vim.Format.Core/Geometry/SceneExtensions.cs b/src/cs/vim/Vim.Format.Core/Geometry/SceneExtensions.cs index f237bcdc..fc58e000 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/SceneExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/SceneExtensions.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using Vim.LinqArray; using Vim.Math3d; namespace Vim.Format.Geometry @@ -14,21 +13,21 @@ public static IEnumerable TransformedMeshes(this IScene scene) => scene.Nodes.Where(n => n.GetMesh() != null).Select(TransformedMesh); public static IMesh MergedGeometry(this IScene scene) - => scene.Nodes.ToEnumerable().MergedGeometry(); + => scene.Nodes.MergedGeometry(); public static IMesh MergedGeometry(this IEnumerable nodes) => nodes.Where(n => n.GetMesh() != null).Select(TransformedMesh).Merge(); public static IEnumerable AllVertices(this IScene scene) - => scene.TransformedMeshes().SelectMany(g => g.Vertices.ToEnumerable()); + => scene.TransformedMeshes().SelectMany(g => g.Vertices); public static AABox BoundingBox(this IScene scene) => AABox.Create(scene.AllVertices()); - public static IArray TransformedVertices(this ISceneNode node) + public static IList TransformedVertices(this ISceneNode node) => node.TransformedMesh()?.Vertices; public static AABox TransformedBoundingBox(this ISceneNode node) - => AABox.Create(node.TransformedVertices()?.ToEnumerable()); + => AABox.Create(node.TransformedVertices()); } } diff --git a/src/cs/vim/Vim.Format.Core/Geometry/Validation.cs b/src/cs/vim/Vim.Format.Core/Geometry/Validation.cs index abf23eef..2bde8a3e 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/Validation.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/Validation.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Generic; +using System.Linq; using Vim.G3d; -using Vim.LinqArray; using Vim.BFastLib; namespace Vim.Format.Geometry { public static class Validation { - public static void ValidateTableRows(this Document doc) + private static void ValidateTableRows(this Document doc) { foreach (var et in doc.EntityTables.Values.ToArray()) { @@ -32,11 +32,11 @@ public static void ValidateTableRows(this Document doc) } } - public static void ValidateIndexColumns(this Document doc) + private static void ValidateIndexColumns(this Document doc) { foreach (var et in doc.EntityTables.Values.ToArray()) { - foreach (var ic in et.IndexColumns.Values.ToEnumerable()) + foreach (var ic in et.IndexColumns.Values) { var table = ic.GetRelatedTable(doc); if (table == null) @@ -45,7 +45,7 @@ public static void ValidateIndexColumns(this Document doc) } } - public static string[] RequiredAttributeNames => new [] + private static string[] RequiredAttributeNames => new [] { // Vertices CommonAttributes.Position, @@ -62,10 +62,10 @@ public static void ValidateIndexColumns(this Document doc) CommonAttributes.InstanceTransform, }; - public static void ValidateGeometryAttributes(this Document doc) + private static void ValidateGeometryAttributes(this Document doc) { var attributes = doc.Geometry.Attributes; - var attributeNameSet = new HashSet(attributes.Select(a => a.Name).ToEnumerable()); + var attributeNameSet = new HashSet(attributes.Select(a => a.Name)); foreach (var attributeName in RequiredAttributeNames) { if (!attributeNameSet.Contains(attributeName)) @@ -73,9 +73,9 @@ public static void ValidateGeometryAttributes(this Document doc) } } - public static void ValidateAssets(this Document doc) + private 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. } @@ -89,9 +89,9 @@ public static void Validate(this Document doc) // TODO: ValidateShapes() to validate VIM files which contain optional 2d data (shapes/overlays). - public static void ValidateIndices(this IMesh mesh) + private static void ValidateIndices(this IMesh mesh) { - foreach (var index in mesh.Indices.ToEnumerable()) + foreach (var index in mesh.Indices) { 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}"); diff --git a/src/cs/vim/Vim.Format.Core/Validation.cs b/src/cs/vim/Vim.Format.Core/Validation.cs index 3b6cda4b..8e97c44d 100644 --- a/src/cs/vim/Vim.Format.Core/Validation.cs +++ b/src/cs/vim/Vim.Format.Core/Validation.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Vim.BFastLib; using Vim.G3d; using Vim.LinqArray; @@ -36,7 +37,7 @@ private static void ValidateIndexColumns(this Document doc) { foreach (var et in doc.EntityTables.Values.ToArray()) { - foreach (var ic in et.IndexColumns.Values.ToEnumerable()) + foreach (var ic in et.IndexColumns.Values) { var table = ic.GetRelatedTable(doc); if (table == null) @@ -65,7 +66,7 @@ private static void ValidateIndexColumns(this Document doc) private static void ValidateGeometryAttributes(this Document doc) { var attributes = doc.Geometry.Attributes; - var attributeNameSet = new HashSet(attributes.Select(a => a.Name).ToEnumerable()); + var attributeNameSet = new HashSet(attributes.Select(a => a.Name)); foreach (var attributeName in RequiredAttributeNames) { if (!attributeNameSet.Contains(attributeName)) @@ -75,7 +76,7 @@ private static void ValidateGeometryAttributes(this Document doc) private 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. } diff --git a/src/cs/vim/Vim.Format.Core/VimSchema.cs b/src/cs/vim/Vim.Format.Core/VimSchema.cs index 42b8e208..e961448b 100644 --- a/src/cs/vim/Vim.Format.Core/VimSchema.cs +++ b/src/cs/vim/Vim.Format.Core/VimSchema.cs @@ -50,7 +50,7 @@ public static VimSchema Create(string filePath) public 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); diff --git a/src/cs/vim/Vim.Format.Tests/EntityTable_v2_Tests.cs b/src/cs/vim/Vim.Format.Tests/EntityTable_v2_Tests.cs index f9d9c28a..96973c1e 100644 --- a/src/cs/vim/Vim.Format.Tests/EntityTable_v2_Tests.cs +++ b/src/cs/vim/Vim.Format.Tests/EntityTable_v2_Tests.cs @@ -21,9 +21,8 @@ public static void TestEntityTable_v2_Parity() var dm = vim.DocumentModel; // EntityTable_v2 manual construction. - var fileInfo = new FileInfo(vimFilePath); - var entityTables = fileInfo.EnumerateEntityTables(false).ToArray(); - var entityTableSet = new EntityTableSet(entityTables, stringBuffer); + var entityTableSet = new EntityTableSet( + SerializableDocument.FromPath(vimFilePath).EntityTables.ToArray(), stringBuffer); var baseElementCount = dm.NumElement; var nextElementCount = entityTableSet.ElementTable.RowCount; diff --git a/src/cs/vim/Vim.Format.Tests/FormatTests.cs b/src/cs/vim/Vim.Format.Tests/FormatTests.cs index cb57dfa6..a9e5adf0 100644 --- a/src/cs/vim/Vim.Format.Tests/FormatTests.cs +++ b/src/cs/vim/Vim.Format.Tests/FormatTests.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using Vim.BFastLib; using Vim.LinqArray; +using Vim.Util; namespace Vim.Format.Tests { @@ -68,9 +69,9 @@ public static void AssertEquals(SerializableDocument d1, SerializableDocument d2 public static void AssertEquals(EntityTable et1, EntityTable et2) { Assert.AreEqual(et1.Name, et2.Name); - Assert.AreEqual(et1.DataColumns.Keys.ToEnumerable().OrderBy(n => n).ToArray(), et2.DataColumns.Keys.ToEnumerable().OrderBy(n => n).ToArray()); - Assert.AreEqual(et1.IndexColumns.Keys.ToEnumerable().OrderBy(n => n).ToArray(), et2.IndexColumns.Keys.ToEnumerable().OrderBy(n => n).ToArray()); - Assert.AreEqual(et1.StringColumns.Keys.ToEnumerable().OrderBy(n => n).ToArray(), et2.StringColumns.Keys.ToEnumerable().OrderBy(n => n).ToArray()); + Assert.AreEqual(et1.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.OrderBy(c => c.Name).ToArray(); var columns2 = et2.Columns.OrderBy(c => c.Name).ToArray(); @@ -104,9 +105,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(); @@ -137,9 +138,9 @@ public static void AssertIsSuperSetOf(Document d1, Document d2, bool skipGeometr 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; @@ -158,8 +159,8 @@ public static void AssertEquals(Document d1, Document d2, bool skipGeometryAndNo var schema2 = VimSchema.Create(d2); 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 index 14c153a6..b9828734 100644 --- a/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs +++ b/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs @@ -3,14 +3,13 @@ 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 XYTriangle = new[] { new Vector3(0f, 0f, 0f), new Vector3(0f, 1f, 0f), new Vector3(1f, 0f, 0f) }.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); @@ -20,7 +19,7 @@ public static class GeometryTests 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()); + TestTetrahedronVertices.TriMesh(TestTetrahedronIndices); //public static IMesh Torus = Primitives.Torus(10, 0.2f, 10, 24); @@ -252,10 +251,9 @@ public static void StripIndicesTests() Assert.AreEqual(3, clockwiseStrip22[1]); Assert.AreEqual(1, clockwiseStrip22[2]); Assert.AreEqual(0, clockwiseStrip22[3]); - var reversed22 = clockwiseStrip22.ToIArray().Reverse(); for (var i = 0; i < strip22.Count; ++i) { - Assert.AreEqual(strip22[i], reversed22[i]); + Assert.AreEqual(strip22[i], clockwiseStrip22[^i]); } // *------*------* @@ -292,10 +290,10 @@ public static void TriangleSerializationTest() var submeshMaterials = new[] { 0 }; var g3d = new G3DBuilder() - .AddVertices(vertices.ToIArray()) - .AddIndices(indices.ToIArray()) - .Add(submeshIndexOffsets.ToIArray().ToSubmeshIndexOffsetAttribute()) - .Add(submeshMaterials.ToIArray().ToSubmeshMaterialAttribute()) + .AddVertices(vertices) + .AddIndices(indices) + .Add(submeshIndexOffsets.ToSubmeshIndexOffsetAttribute()) + .Add(submeshMaterials.ToSubmeshMaterialAttribute()) .ToG3D(); var bfast = g3d.ToBFast(); @@ -310,8 +308,8 @@ public static void TriangleSerializationTest() 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.SubmeshIndexOffsets.Single()); + Assert.AreEqual(0, mesh.SubmeshMaterials.Single()); Assert.AreEqual(0, mesh.GetFaceMaterials().First()); } } diff --git a/src/cs/vim/Vim.Format/Merge/MergeService.cs b/src/cs/vim/Vim.Format/Merge/MergeService.cs index ce41cd29..e8dbbfc3 100644 --- a/src/cs/vim/Vim.Format/Merge/MergeService.cs +++ b/src/cs/vim/Vim.Format/Merge/MergeService.cs @@ -6,10 +6,9 @@ using System.Threading.Tasks; using Vim.BFastLib; using Vim.Format.Geometry; -using Vim.LinqArray; using Vim.Math3d; using Vim.Format.ObjectModel; - +using Vim.G3d; using Vim.Util; namespace Vim.Format.Merge @@ -135,11 +134,11 @@ public static DocumentBuilder MergeVimScenes( progress?.Report("Merging geometry"); ct.ThrowIfCancellationRequested(); - var materialCounts = vims.Select(v => v.Materials.Count); - var materialOffsets = materialCounts.ToIArray().PostAccumulate((x, y) => x + y).DropLast(); + var materialCounts = vims.Select(v => v.Materials.Count).ToArray(); + var materialOffsets = materialCounts.PostAccumulate((x, y) => x + y).DropLast(); db.Geometry.AddMeshes(vims - .SelectMany((vim, vimIndex) => vim.Meshes.Select(mesh => (mesh, vimIndex)).ToEnumerable()) + .SelectMany((vim, vimIndex) => vim.Meshes.Select(mesh => (mesh, vimIndex))) .Select( pair => new DocumentBuilder.SubdividedMesh( indices: pair.mesh.Indices?.ToList(), @@ -162,8 +161,8 @@ public static DocumentBuilder MergeVimScenes( vimTransforms = gridTransforms.Zip(vimTransforms, (g, t) => g * t).ToArray(); } - var meshCounts = vims.Select(v => v.Meshes.Count); - var meshOffsets = meshCounts.ToIArray().PostAccumulate((x, y) => x + y).DropLast(); + var meshCounts = vims.Select(v => v.Meshes.Count).ToArray(); + var meshOffsets = meshCounts.PostAccumulate((x, y) => x + y).DropLast(); // Merge the instances progress?.Report("Merging instances"); @@ -172,7 +171,7 @@ public static DocumentBuilder MergeVimScenes( var allIdentity = vimTransforms.All(t => t.IsIdentity); db.Geometry.AddInstances( vims - .SelectMany((vim, vimIndex) => vim.VimNodes.Select(node => (node, vimIndex)).ToEnumerable()) + .SelectMany((vim, vimIndex) => vim.VimNodes.Select(node => (node, vimIndex))) .Select(pair => new DocumentBuilder.Instance() { ParentIndex = -1, @@ -186,7 +185,7 @@ public static DocumentBuilder MergeVimScenes( // Merge the assets progress?.Report("Merging assets"); ct.ThrowIfCancellationRequested(); - foreach (var asset in vims.SelectMany(vim => vim.Document.Assets.Values.ToEnumerable())) + foreach (var asset in vims.SelectMany(vim => vim.Document.Assets.Values)) db.AddAsset(asset); return db; @@ -225,7 +224,7 @@ private static void ValidateSameObjectModelSchemaMajorVersion(VimScene[] vims) private static Matrix4x4[] GetGridTransforms(VimScene[] vims, float padding) { var boxes = vims.Select(v => v.BoundingBox()).ToArray(); - var centerBottomTransforms = boxes.Select(b => Matrix4x4.CreateTranslation(-b.CenterBottom)).ToIArray(); + var centerBottomTransforms = boxes.Select(b => Matrix4x4.CreateTranslation(-b.CenterBottom)).ToArray(); var columnSize = boxes.Select(b => b.Extent.X).Max() + padding; var rowSize = boxes.Select(b => b.Extent.Y).Max() + padding; @@ -243,7 +242,7 @@ private static Matrix4x4[] GetGridTransforms(VimScene[] vims, float padding) return transforms; } - private static IArray GetGridOfTransforms(int count, int numRows, float xSide, float ySide) + private static IList GetGridOfTransforms(int count, int numRows, float xSide, float ySide) => count.Select(i => Matrix4x4.CreateTranslation(i % numRows * xSide, i / numRows * ySide, 0)); private static void MergeEntities( @@ -261,7 +260,7 @@ private static void MergeEntities( { cancellationToken.ThrowIfCancellationRequested(); - foreach (var entityTable in vim.Document.EntityTables.Values.ToEnumerable()) + foreach (var entityTable in vim.Document.EntityTables.Values) { cancellationToken.ThrowIfCancellationRequested(); @@ -299,7 +298,7 @@ private static Dictionary ComputeMergedEntityTableOffsets(IEnu var entityTableOffsetMap = new Dictionary(); foreach (var doc in documents) { - foreach (var entityTable in doc.EntityTables.Values.ToEnumerable()) + foreach (var entityTable in doc.EntityTables.Values) { var entityTableName = entityTable.Name; diff --git a/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs b/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs index b7b3af36..8dd3db31 100644 --- a/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs +++ b/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs @@ -25,7 +25,7 @@ public void AddTable(EntityTable entityTable, Dictionary entit Debug.Assert(entityIndexOffsets[entityTable] == NumRows); // Add index columns from the entity table - foreach (var k in entityTable.IndexColumns.Keys.ToEnumerable()) + foreach (var k in entityTable.IndexColumns.Keys) { var col = entityTable.IndexColumns[k]; var indexColumnFullName = col.Name; @@ -43,7 +43,7 @@ public void AddTable(EntityTable entityTable, Dictionary entit } // Add data columns from the entity table - foreach (var colName in entityTable.DataColumns.Keys.ToEnumerable()) + foreach (var colName in entityTable.DataColumns.Keys) { var col = entityTable.DataColumns[colName]; if (!DataColumns.ContainsKey(colName)) @@ -58,7 +58,7 @@ public void AddTable(EntityTable entityTable, Dictionary entit } // Add string columns from the entity table - foreach (var k in entityTable.StringColumns.Keys.ToEnumerable()) + foreach (var k in entityTable.StringColumns.Keys) { if (!StringColumns.ContainsKey(k)) StringColumns.Add(k, Enumerable.Repeat("", NumRows).ToList()); @@ -75,7 +75,7 @@ public void AddTable(EntityTable entityTable, Dictionary entit { var colName = kv.Key; var typePrefix = colName.GetTypePrefix(); - if (!entityTable.DataColumns.Contains(colName)) + if (!entityTable.DataColumns.ContainsKey(colName)) { var cur = DataColumns[colName]; var defaultBuffer = ColumnExtensions.CreateDefaultDataColumnBuffer(entityTable.NumRows, typePrefix); @@ -85,13 +85,13 @@ public void AddTable(EntityTable entityTable, Dictionary entit foreach (var kv in IndexColumns) { - if (!entityTable.IndexColumns.Contains(kv.Key)) + if (!entityTable.IndexColumns.ContainsKey(kv.Key)) IndexColumns[kv.Key].AddRange(Enumerable.Repeat(-1, entityTable.NumRows)); } foreach (var kv in StringColumns) { - if (!entityTable.StringColumns.Contains(kv.Key)) + if (!entityTable.StringColumns.ContainsKey(kv.Key)) StringColumns[kv.Key].AddRange(Enumerable.Repeat("", entityTable.NumRows)); } diff --git a/src/cs/vim/Vim.Format/ObjectModel/ElementInfo.cs b/src/cs/vim/Vim.Format/ObjectModel/ElementInfo.cs index f8f40bac..d44c75de 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/ElementInfo.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/ElementInfo.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Vim.LinqArray; namespace Vim.Format.ObjectModel diff --git a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelExtensions.cs b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelExtensions.cs index ceab0b2c..df5d2634 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelExtensions.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelExtensions.cs @@ -3,6 +3,7 @@ using System.Data; using System.IO; using System.Linq; +using Vim.G3d; using Vim.Util; using Vim.LinqArray; @@ -160,7 +161,8 @@ public static DataTable GetScheduleAsDataTable(this DocumentModel dm, int schedu var columnSet = new HashSet(columns.Select(c => c.Index)); var cellRecords = dm.ScheduleCellScheduleColumnIndex - .IndicesWhere((colIndex, _) => columnSet.Contains(colIndex)) + .Indices() + .Where((colIndex, _) => columnSet.Contains(colIndex)) .Select(cellIndex => new CellData( dm.GetScheduleCellValue(cellIndex), dm.GetScheduleCellScheduleColumnIndex(cellIndex), diff --git a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs index d45efcb3..50f7c8af 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs @@ -2967,12 +2967,12 @@ public Camera GetCamera(int n) public Double GetMaterialTransparency(int index, Double defaultValue = default) => MaterialTransparency?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; public IArray MaterialColorTextureFileIndex { get; } public int GetMaterialColorTextureFileIndex(int index) => MaterialColorTextureFileIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray MaterialNormalTextureFileIndex { get; } + public IList MaterialNormalTextureFileIndex { get; } public int GetMaterialNormalTextureFileIndex(int index) => MaterialNormalTextureFileIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray MaterialElementIndex { get; } + public IList 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 IList MaterialList { get; } public Material GetMaterial(int n) { if (n < 0) return null; @@ -3007,18 +3007,18 @@ public Material GetMaterial(int n) public EntityTable MaterialInElementEntityTable { get; } - public IArray MaterialInElementArea { get; } + public IList MaterialInElementArea { get; } public Double GetMaterialInElementArea(int index, Double defaultValue = default) => MaterialInElementArea?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialInElementVolume { get; } + public IList MaterialInElementVolume { get; } public Double GetMaterialInElementVolume(int index, Double defaultValue = default) => MaterialInElementVolume?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialInElementIsPaint { get; } + public IList MaterialInElementIsPaint { get; } public Boolean GetMaterialInElementIsPaint(int index, Boolean defaultValue = default) => MaterialInElementIsPaint?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialInElementMaterialIndex { get; } + public IList MaterialInElementMaterialIndex { get; } public int GetMaterialInElementMaterialIndex(int index) => MaterialInElementMaterialIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray MaterialInElementElementIndex { get; } + public IList 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 IList MaterialInElementList { get; } public MaterialInElement GetMaterialInElement(int n) { if (n < 0) return null; @@ -3038,18 +3038,18 @@ public MaterialInElement GetMaterialInElement(int n) public EntityTable CompoundStructureLayerEntityTable { get; } - public IArray CompoundStructureLayerOrderIndex { get; } + public IList CompoundStructureLayerOrderIndex { get; } public Int32 GetCompoundStructureLayerOrderIndex(int index, Int32 defaultValue = default) => CompoundStructureLayerOrderIndex?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CompoundStructureLayerWidth { get; } + public IList CompoundStructureLayerWidth { get; } public Double GetCompoundStructureLayerWidth(int index, Double defaultValue = default) => CompoundStructureLayerWidth?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CompoundStructureLayerMaterialFunctionAssignment { get; } + public IList CompoundStructureLayerMaterialFunctionAssignment { get; } public String GetCompoundStructureLayerMaterialFunctionAssignment(int index, String defaultValue = "") => CompoundStructureLayerMaterialFunctionAssignment?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CompoundStructureLayerMaterialIndex { get; } + public IList CompoundStructureLayerMaterialIndex { get; } public int GetCompoundStructureLayerMaterialIndex(int index) => CompoundStructureLayerMaterialIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray CompoundStructureLayerCompoundStructureIndex { get; } + public IList 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 IList CompoundStructureLayerList { get; } public CompoundStructureLayer GetCompoundStructureLayer(int n) { if (n < 0) return null; @@ -3069,12 +3069,12 @@ public CompoundStructureLayer GetCompoundStructureLayer(int n) public EntityTable CompoundStructureEntityTable { get; } - public IArray CompoundStructureWidth { get; } + public IList CompoundStructureWidth { get; } public Double GetCompoundStructureWidth(int index, Double defaultValue = default) => CompoundStructureWidth?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CompoundStructureStructuralLayerIndex { get; } + public IList 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 IList CompoundStructureList { get; } public CompoundStructure GetCompoundStructure(int n) { if (n < 0) return null; @@ -3091,10 +3091,10 @@ public CompoundStructure GetCompoundStructure(int n) public EntityTable NodeEntityTable { get; } - public IArray NodeElementIndex { get; } + public IList 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 IList NodeList { get; } public Node GetNode(int n) { if (n < 0) return null; @@ -3110,24 +3110,24 @@ public Node GetNode(int n) public EntityTable GeometryEntityTable { get; } - public IArray GeometryBox_Min_X { get; } + public IList 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 IList 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 IList 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 IList 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 IList 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 IList 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 IList GeometryVertexCount { get; } public Int32 GetGeometryVertexCount(int index, Int32 defaultValue = default) => GeometryVertexCount?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GeometryFaceCount { get; } + public IList 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 IList GeometryList { get; } public Geometry GetGeometry(int n) { if (n < 0) return null; @@ -3150,10 +3150,10 @@ public Geometry GetGeometry(int n) public EntityTable ShapeEntityTable { get; } - public IArray ShapeElementIndex { get; } + public IList 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 IList ShapeList { get; } public Shape GetShape(int n) { if (n < 0) return null; @@ -3169,10 +3169,10 @@ public Shape GetShape(int n) public EntityTable ShapeCollectionEntityTable { get; } - public IArray ShapeCollectionElementIndex { get; } + public IList 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 IList ShapeCollectionList { get; } public ShapeCollection GetShapeCollection(int n) { if (n < 0) return null; @@ -3188,12 +3188,12 @@ public ShapeCollection GetShapeCollection(int n) public EntityTable ShapeInShapeCollectionEntityTable { get; } - public IArray ShapeInShapeCollectionShapeIndex { get; } + public IList ShapeInShapeCollectionShapeIndex { get; } public int GetShapeInShapeCollectionShapeIndex(int index) => ShapeInShapeCollectionShapeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ShapeInShapeCollectionShapeCollectionIndex { get; } + public IList 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 IList ShapeInShapeCollectionList { get; } public ShapeInShapeCollection GetShapeInShapeCollection(int n) { if (n < 0) return null; @@ -3210,14 +3210,14 @@ public ShapeInShapeCollection GetShapeInShapeCollection(int n) public EntityTable SystemEntityTable { get; } - public IArray SystemSystemType { get; } + public IList SystemSystemType { get; } public Int32 GetSystemSystemType(int index, Int32 defaultValue = default) => SystemSystemType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray SystemFamilyTypeIndex { get; } + public IList SystemFamilyTypeIndex { get; } public int GetSystemFamilyTypeIndex(int index) => SystemFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray SystemElementIndex { get; } + public IList 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 IList SystemList { get; } public System GetSystem(int n) { if (n < 0) return null; @@ -3235,14 +3235,14 @@ public System GetSystem(int n) public EntityTable ElementInSystemEntityTable { get; } - public IArray ElementInSystemRoles { get; } + public IList ElementInSystemRoles { get; } public Int32 GetElementInSystemRoles(int index, Int32 defaultValue = default) => ElementInSystemRoles?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementInSystemSystemIndex { get; } + public IList ElementInSystemSystemIndex { get; } public int GetElementInSystemSystemIndex(int index) => ElementInSystemSystemIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementInSystemElementIndex { get; } + public IList 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 IList ElementInSystemList { get; } public ElementInSystem GetElementInSystem(int n) { if (n < 0) return null; @@ -3260,16 +3260,16 @@ public ElementInSystem GetElementInSystem(int n) public EntityTable WarningEntityTable { get; } - public IArray WarningGuid { get; } + public IList WarningGuid { get; } public String GetWarningGuid(int index, String defaultValue = "") => WarningGuid?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WarningSeverity { get; } + public IList WarningSeverity { get; } public String GetWarningSeverity(int index, String defaultValue = "") => WarningSeverity?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WarningDescription { get; } + public IList WarningDescription { get; } public String GetWarningDescription(int index, String defaultValue = "") => WarningDescription?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WarningBimDocumentIndex { get; } + public IList 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 IList WarningList { get; } public Warning GetWarning(int n) { if (n < 0) return null; @@ -3288,12 +3288,12 @@ public Warning GetWarning(int n) public EntityTable ElementInWarningEntityTable { get; } - public IArray ElementInWarningWarningIndex { get; } + public IList ElementInWarningWarningIndex { get; } public int GetElementInWarningWarningIndex(int index) => ElementInWarningWarningIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementInWarningElementIndex { get; } + public IList 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 IList ElementInWarningList { get; } public ElementInWarning GetElementInWarning(int n) { if (n < 0) return null; @@ -3310,24 +3310,24 @@ public ElementInWarning GetElementInWarning(int n) public EntityTable BasePointEntityTable { get; } - public IArray BasePointIsSurveyPoint { get; } + public IList BasePointIsSurveyPoint { get; } public Boolean GetBasePointIsSurveyPoint(int index, Boolean defaultValue = default) => BasePointIsSurveyPoint?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BasePointPosition_X { get; } + public IList BasePointPosition_X { get; } public Double GetBasePointPosition_X(int index, Double defaultValue = default) => BasePointPosition_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BasePointPosition_Y { get; } + public IList BasePointPosition_Y { get; } public Double GetBasePointPosition_Y(int index, Double defaultValue = default) => BasePointPosition_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BasePointPosition_Z { get; } + public IList BasePointPosition_Z { get; } public Double GetBasePointPosition_Z(int index, Double defaultValue = default) => BasePointPosition_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BasePointSharedPosition_X { get; } + public IList BasePointSharedPosition_X { get; } public Double GetBasePointSharedPosition_X(int index, Double defaultValue = default) => BasePointSharedPosition_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BasePointSharedPosition_Y { get; } + public IList BasePointSharedPosition_Y { get; } public Double GetBasePointSharedPosition_Y(int index, Double defaultValue = default) => BasePointSharedPosition_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BasePointSharedPosition_Z { get; } + public IList BasePointSharedPosition_Z { get; } public Double GetBasePointSharedPosition_Z(int index, Double defaultValue = default) => BasePointSharedPosition_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BasePointElementIndex { get; } + public IList 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 IList BasePointList { get; } public BasePoint GetBasePoint(int n) { if (n < 0) return null; @@ -3350,18 +3350,18 @@ public BasePoint GetBasePoint(int n) public EntityTable PhaseFilterEntityTable { get; } - public IArray PhaseFilterNew { get; } + public IList PhaseFilterNew { get; } public Int32 GetPhaseFilterNew(int index, Int32 defaultValue = default) => PhaseFilterNew?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray PhaseFilterExisting { get; } + public IList PhaseFilterExisting { get; } public Int32 GetPhaseFilterExisting(int index, Int32 defaultValue = default) => PhaseFilterExisting?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray PhaseFilterDemolished { get; } + public IList PhaseFilterDemolished { get; } public Int32 GetPhaseFilterDemolished(int index, Int32 defaultValue = default) => PhaseFilterDemolished?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray PhaseFilterTemporary { get; } + public IList PhaseFilterTemporary { get; } public Int32 GetPhaseFilterTemporary(int index, Int32 defaultValue = default) => PhaseFilterTemporary?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray PhaseFilterElementIndex { get; } + public IList 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 IList PhaseFilterList { get; } public PhaseFilter GetPhaseFilter(int n) { if (n < 0) return null; @@ -3381,38 +3381,38 @@ public PhaseFilter GetPhaseFilter(int n) public EntityTable GridEntityTable { get; } - public IArray GridStartPoint_X { get; } + public IList GridStartPoint_X { get; } public Double GetGridStartPoint_X(int index, Double defaultValue = default) => GridStartPoint_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridStartPoint_Y { get; } + public IList GridStartPoint_Y { get; } public Double GetGridStartPoint_Y(int index, Double defaultValue = default) => GridStartPoint_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridStartPoint_Z { get; } + public IList GridStartPoint_Z { get; } public Double GetGridStartPoint_Z(int index, Double defaultValue = default) => GridStartPoint_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridEndPoint_X { get; } + public IList GridEndPoint_X { get; } public Double GetGridEndPoint_X(int index, Double defaultValue = default) => GridEndPoint_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridEndPoint_Y { get; } + public IList GridEndPoint_Y { get; } public Double GetGridEndPoint_Y(int index, Double defaultValue = default) => GridEndPoint_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridEndPoint_Z { get; } + public IList GridEndPoint_Z { get; } public Double GetGridEndPoint_Z(int index, Double defaultValue = default) => GridEndPoint_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridIsCurved { get; } + public IList GridIsCurved { get; } public Boolean GetGridIsCurved(int index, Boolean defaultValue = default) => GridIsCurved?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GridExtents_Min_X { get; } + public IList 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 IList 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 IList 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 IList 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 IList 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 IList 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 IList GridFamilyTypeIndex { get; } public int GetGridFamilyTypeIndex(int index) => GridFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray GridElementIndex { get; } + public IList 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 IList GridList { get; } public Grid GetGrid(int n) { if (n < 0) return null; @@ -3442,20 +3442,20 @@ public Grid GetGrid(int n) public EntityTable AreaEntityTable { get; } - public IArray AreaValue { get; } + public IList AreaValue { get; } public Double GetAreaValue(int index, Double defaultValue = default) => AreaValue?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AreaPerimeter { get; } + public IList AreaPerimeter { get; } public Double GetAreaPerimeter(int index, Double defaultValue = default) => AreaPerimeter?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AreaNumber { get; } + public IList AreaNumber { get; } public String GetAreaNumber(int index, String defaultValue = "") => AreaNumber?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AreaIsGrossInterior { get; } + public IList AreaIsGrossInterior { get; } public Boolean GetAreaIsGrossInterior(int index, Boolean defaultValue = default) => AreaIsGrossInterior?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AreaAreaSchemeIndex { get; } + public IList AreaAreaSchemeIndex { get; } public int GetAreaAreaSchemeIndex(int index) => AreaAreaSchemeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray AreaElementIndex { get; } + public IList 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 IList AreaList { get; } public Area GetArea(int n) { if (n < 0) return null; @@ -3476,12 +3476,12 @@ public Area GetArea(int n) public EntityTable AreaSchemeEntityTable { get; } - public IArray AreaSchemeIsGrossBuildingArea { get; } + public IList AreaSchemeIsGrossBuildingArea { get; } public Boolean GetAreaSchemeIsGrossBuildingArea(int index, Boolean defaultValue = default) => AreaSchemeIsGrossBuildingArea?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AreaSchemeElementIndex { get; } + public IList 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 IList AreaSchemeList { get; } public AreaScheme GetAreaScheme(int n) { if (n < 0) return null; @@ -3498,10 +3498,10 @@ public AreaScheme GetAreaScheme(int n) public EntityTable ScheduleEntityTable { get; } - public IArray ScheduleElementIndex { get; } + public IList 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 IList ScheduleList { get; } public Schedule GetSchedule(int n) { if (n < 0) return null; @@ -3517,14 +3517,14 @@ public Schedule GetSchedule(int n) public EntityTable ScheduleColumnEntityTable { get; } - public IArray ScheduleColumnName { get; } + public IList ScheduleColumnName { get; } public String GetScheduleColumnName(int index, String defaultValue = "") => ScheduleColumnName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ScheduleColumnColumnIndex { get; } + public IList ScheduleColumnColumnIndex { get; } public Int32 GetScheduleColumnColumnIndex(int index, Int32 defaultValue = default) => ScheduleColumnColumnIndex?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ScheduleColumnScheduleIndex { get; } + public IList 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 IList ScheduleColumnList { get; } public ScheduleColumn GetScheduleColumn(int n) { if (n < 0) return null; @@ -3542,14 +3542,14 @@ public ScheduleColumn GetScheduleColumn(int n) public EntityTable ScheduleCellEntityTable { get; } - public IArray ScheduleCellValue { get; } + public IList ScheduleCellValue { get; } public String GetScheduleCellValue(int index, String defaultValue = "") => ScheduleCellValue?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ScheduleCellRowIndex { get; } + public IList ScheduleCellRowIndex { get; } public Int32 GetScheduleCellRowIndex(int index, Int32 defaultValue = default) => ScheduleCellRowIndex?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ScheduleCellScheduleColumnIndex { get; } + public IList 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 IList ScheduleCellList { get; } public ScheduleCell GetScheduleCell(int n) { if (n < 0) return null; @@ -3567,10 +3567,10 @@ public ScheduleCell GetScheduleCell(int n) public EntityTable ViewSheetSetEntityTable { get; } - public IArray ViewSheetSetElementIndex { get; } + public IList 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 IList ViewSheetSetList { get; } public ViewSheetSet GetViewSheetSet(int n) { if (n < 0) return null; @@ -3586,12 +3586,12 @@ public ViewSheetSet GetViewSheetSet(int n) public EntityTable ViewSheetEntityTable { get; } - public IArray ViewSheetFamilyTypeIndex { get; } + public IList ViewSheetFamilyTypeIndex { get; } public int GetViewSheetFamilyTypeIndex(int index) => ViewSheetFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ViewSheetElementIndex { get; } + public IList 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 IList ViewSheetList { get; } public ViewSheet GetViewSheet(int n) { if (n < 0) return null; @@ -3608,12 +3608,12 @@ public ViewSheet GetViewSheet(int n) public EntityTable ViewSheetInViewSheetSetEntityTable { get; } - public IArray ViewSheetInViewSheetSetViewSheetIndex { get; } + public IList ViewSheetInViewSheetSetViewSheetIndex { get; } public int GetViewSheetInViewSheetSetViewSheetIndex(int index) => ViewSheetInViewSheetSetViewSheetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ViewSheetInViewSheetSetViewSheetSetIndex { get; } + public IList 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 IList ViewSheetInViewSheetSetList { get; } public ViewSheetInViewSheetSet GetViewSheetInViewSheetSet(int n) { if (n < 0) return null; @@ -3630,12 +3630,12 @@ public ViewSheetInViewSheetSet GetViewSheetInViewSheetSet(int n) public EntityTable ViewInViewSheetSetEntityTable { get; } - public IArray ViewInViewSheetSetViewIndex { get; } + public IList ViewInViewSheetSetViewIndex { get; } public int GetViewInViewSheetSetViewIndex(int index) => ViewInViewSheetSetViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ViewInViewSheetSetViewSheetSetIndex { get; } + public IList 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 IList ViewInViewSheetSetList { get; } public ViewInViewSheetSet GetViewInViewSheetSet(int n) { if (n < 0) return null; @@ -3652,12 +3652,12 @@ public ViewInViewSheetSet GetViewInViewSheetSet(int n) public EntityTable ViewInViewSheetEntityTable { get; } - public IArray ViewInViewSheetViewIndex { get; } + public IList ViewInViewSheetViewIndex { get; } public int GetViewInViewSheetViewIndex(int index) => ViewInViewSheetViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ViewInViewSheetViewSheetIndex { get; } + public IList 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 IList ViewInViewSheetList { get; } public ViewInViewSheet GetViewInViewSheet(int n) { if (n < 0) return null; @@ -3674,20 +3674,20 @@ public ViewInViewSheet GetViewInViewSheet(int n) public EntityTable SiteEntityTable { get; } - public IArray SiteLatitude { get; } + public IList SiteLatitude { get; } public Double GetSiteLatitude(int index, Double defaultValue = default) => SiteLatitude?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray SiteLongitude { get; } + public IList SiteLongitude { get; } public Double GetSiteLongitude(int index, Double defaultValue = default) => SiteLongitude?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray SiteAddress { get; } + public IList SiteAddress { get; } public String GetSiteAddress(int index, String defaultValue = "") => SiteAddress?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray SiteElevation { get; } + public IList SiteElevation { get; } public Double GetSiteElevation(int index, Double defaultValue = default) => SiteElevation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray SiteNumber { get; } + public IList SiteNumber { get; } public String GetSiteNumber(int index, String defaultValue = "") => SiteNumber?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray SiteElementIndex { get; } + public IList 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 IList SiteList { get; } public Site GetSite(int n) { if (n < 0) return null; @@ -3708,18 +3708,18 @@ public Site GetSite(int n) public EntityTable BuildingEntityTable { get; } - public IArray BuildingElevation { get; } + public IList BuildingElevation { get; } public Double GetBuildingElevation(int index, Double defaultValue = default) => BuildingElevation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BuildingTerrainElevation { get; } + public IList BuildingTerrainElevation { get; } public Double GetBuildingTerrainElevation(int index, Double defaultValue = default) => BuildingTerrainElevation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BuildingAddress { get; } + public IList BuildingAddress { get; } public String GetBuildingAddress(int index, String defaultValue = "") => BuildingAddress?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BuildingSiteIndex { get; } + public IList BuildingSiteIndex { get; } public int GetBuildingSiteIndex(int index) => BuildingSiteIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray BuildingElementIndex { get; } + public IList 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 IList BuildingList { get; } public Building GetBuilding(int n) { if (n < 0) return null; @@ -3910,335 +3910,335 @@ public DocumentModel(Document d, bool inParallel = true) BuildingEntityTable = Document.GetTable("Vim.Building"); // Initialize entity arrays - AssetBufferName = AssetEntityTable?.GetStringColumnValues("string:BufferName") ?? Array.Empty().ToIArray(); - DisplayUnitSpec = DisplayUnitEntityTable?.GetStringColumnValues("string:Spec") ?? Array.Empty().ToIArray(); - DisplayUnitType = DisplayUnitEntityTable?.GetStringColumnValues("string:Type") ?? Array.Empty().ToIArray(); - DisplayUnitLabel = DisplayUnitEntityTable?.GetStringColumnValues("string:Label") ?? Array.Empty().ToIArray(); - ParameterDescriptorName = ParameterDescriptorEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty().ToIArray(); - ParameterDescriptorGroup = ParameterDescriptorEntityTable?.GetStringColumnValues("string:Group") ?? Array.Empty().ToIArray(); - ParameterDescriptorParameterType = ParameterDescriptorEntityTable?.GetStringColumnValues("string:ParameterType") ?? Array.Empty().ToIArray(); - ParameterDescriptorIsInstance = ParameterDescriptorEntityTable?.GetDataColumnValues("byte:IsInstance") ?? Array.Empty().ToIArray(); - ParameterDescriptorIsShared = ParameterDescriptorEntityTable?.GetDataColumnValues("byte:IsShared") ?? Array.Empty().ToIArray(); - ParameterDescriptorIsReadOnly = ParameterDescriptorEntityTable?.GetDataColumnValues("byte:IsReadOnly") ?? Array.Empty().ToIArray(); - ParameterDescriptorFlags = ParameterDescriptorEntityTable?.GetDataColumnValues("int:Flags") ?? Array.Empty().ToIArray(); - ParameterDescriptorGuid = ParameterDescriptorEntityTable?.GetStringColumnValues("string:Guid") ?? Array.Empty().ToIArray(); - ParameterDescriptorStorageType = ParameterDescriptorEntityTable?.GetDataColumnValues("int:StorageType") ?? Array.Empty().ToIArray(); - ParameterValue = ParameterEntityTable?.GetStringColumnValues("string:Value") ?? Array.Empty().ToIArray(); - ElementId = (ElementEntityTable?.GetDataColumnValues("long:Id") ?? ElementEntityTable?.GetDataColumnValues("int:Id")?.Select(v => (Int64) v)) ?? Array.Empty().ToIArray(); - ElementType = ElementEntityTable?.GetStringColumnValues("string:Type") ?? Array.Empty().ToIArray(); - ElementName = ElementEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty().ToIArray(); - ElementUniqueId = ElementEntityTable?.GetStringColumnValues("string:UniqueId") ?? Array.Empty().ToIArray(); - ElementLocation_X = ElementEntityTable?.GetDataColumnValues("float:Location.X") ?? Array.Empty().ToIArray(); - ElementLocation_Y = ElementEntityTable?.GetDataColumnValues("float:Location.Y") ?? Array.Empty().ToIArray(); - ElementLocation_Z = ElementEntityTable?.GetDataColumnValues("float:Location.Z") ?? Array.Empty().ToIArray(); - ElementFamilyName = ElementEntityTable?.GetStringColumnValues("string:FamilyName") ?? Array.Empty().ToIArray(); - ElementIsPinned = ElementEntityTable?.GetDataColumnValues("byte:IsPinned") ?? Array.Empty().ToIArray(); - WorksetId = WorksetEntityTable?.GetDataColumnValues("int:Id") ?? Array.Empty().ToIArray(); - WorksetName = WorksetEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty().ToIArray(); - WorksetKind = WorksetEntityTable?.GetStringColumnValues("string:Kind") ?? Array.Empty().ToIArray(); - WorksetIsOpen = WorksetEntityTable?.GetDataColumnValues("byte:IsOpen") ?? Array.Empty().ToIArray(); - WorksetIsEditable = WorksetEntityTable?.GetDataColumnValues("byte:IsEditable") ?? Array.Empty().ToIArray(); - WorksetOwner = WorksetEntityTable?.GetStringColumnValues("string:Owner") ?? Array.Empty().ToIArray(); - WorksetUniqueId = WorksetEntityTable?.GetStringColumnValues("string:UniqueId") ?? Array.Empty().ToIArray(); - AssemblyInstanceAssemblyTypeName = AssemblyInstanceEntityTable?.GetStringColumnValues("string:AssemblyTypeName") ?? Array.Empty().ToIArray(); - AssemblyInstancePosition_X = AssemblyInstanceEntityTable?.GetDataColumnValues("float:Position.X") ?? Array.Empty().ToIArray(); - AssemblyInstancePosition_Y = AssemblyInstanceEntityTable?.GetDataColumnValues("float:Position.Y") ?? Array.Empty().ToIArray(); - AssemblyInstancePosition_Z = AssemblyInstanceEntityTable?.GetDataColumnValues("float:Position.Z") ?? Array.Empty().ToIArray(); - GroupGroupType = GroupEntityTable?.GetStringColumnValues("string:GroupType") ?? Array.Empty().ToIArray(); - GroupPosition_X = GroupEntityTable?.GetDataColumnValues("float:Position.X") ?? Array.Empty().ToIArray(); - GroupPosition_Y = GroupEntityTable?.GetDataColumnValues("float:Position.Y") ?? Array.Empty().ToIArray(); - GroupPosition_Z = GroupEntityTable?.GetDataColumnValues("float:Position.Z") ?? Array.Empty().ToIArray(); - DesignOptionIsPrimary = DesignOptionEntityTable?.GetDataColumnValues("byte:IsPrimary") ?? Array.Empty().ToIArray(); - LevelElevation = LevelEntityTable?.GetDataColumnValues("double:Elevation") ?? Array.Empty().ToIArray(); - RoomBaseOffset = RoomEntityTable?.GetDataColumnValues("double:BaseOffset") ?? Array.Empty().ToIArray(); - RoomLimitOffset = RoomEntityTable?.GetDataColumnValues("double:LimitOffset") ?? Array.Empty().ToIArray(); - RoomUnboundedHeight = RoomEntityTable?.GetDataColumnValues("double:UnboundedHeight") ?? Array.Empty().ToIArray(); - RoomVolume = RoomEntityTable?.GetDataColumnValues("double:Volume") ?? Array.Empty().ToIArray(); - RoomPerimeter = RoomEntityTable?.GetDataColumnValues("double:Perimeter") ?? Array.Empty().ToIArray(); - RoomArea = RoomEntityTable?.GetDataColumnValues("double:Area") ?? Array.Empty().ToIArray(); - RoomNumber = RoomEntityTable?.GetStringColumnValues("string:Number") ?? Array.Empty().ToIArray(); - BimDocumentTitle = BimDocumentEntityTable?.GetStringColumnValues("string:Title") ?? Array.Empty().ToIArray(); - BimDocumentIsMetric = BimDocumentEntityTable?.GetDataColumnValues("byte:IsMetric") ?? Array.Empty().ToIArray(); - BimDocumentGuid = BimDocumentEntityTable?.GetStringColumnValues("string:Guid") ?? Array.Empty().ToIArray(); - BimDocumentNumSaves = BimDocumentEntityTable?.GetDataColumnValues("int:NumSaves") ?? Array.Empty().ToIArray(); - BimDocumentIsLinked = BimDocumentEntityTable?.GetDataColumnValues("byte:IsLinked") ?? Array.Empty().ToIArray(); - BimDocumentIsDetached = BimDocumentEntityTable?.GetDataColumnValues("byte:IsDetached") ?? Array.Empty().ToIArray(); - BimDocumentIsWorkshared = BimDocumentEntityTable?.GetDataColumnValues("byte:IsWorkshared") ?? Array.Empty().ToIArray(); - BimDocumentPathName = BimDocumentEntityTable?.GetStringColumnValues("string:PathName") ?? Array.Empty().ToIArray(); - BimDocumentLatitude = BimDocumentEntityTable?.GetDataColumnValues("double:Latitude") ?? Array.Empty().ToIArray(); - BimDocumentLongitude = BimDocumentEntityTable?.GetDataColumnValues("double:Longitude") ?? Array.Empty().ToIArray(); - BimDocumentTimeZone = BimDocumentEntityTable?.GetDataColumnValues("double:TimeZone") ?? Array.Empty().ToIArray(); - BimDocumentPlaceName = BimDocumentEntityTable?.GetStringColumnValues("string:PlaceName") ?? Array.Empty().ToIArray(); - BimDocumentWeatherStationName = BimDocumentEntityTable?.GetStringColumnValues("string:WeatherStationName") ?? Array.Empty().ToIArray(); - BimDocumentElevation = BimDocumentEntityTable?.GetDataColumnValues("double:Elevation") ?? Array.Empty().ToIArray(); - BimDocumentProjectLocation = BimDocumentEntityTable?.GetStringColumnValues("string:ProjectLocation") ?? Array.Empty().ToIArray(); - BimDocumentIssueDate = BimDocumentEntityTable?.GetStringColumnValues("string:IssueDate") ?? Array.Empty().ToIArray(); - BimDocumentStatus = BimDocumentEntityTable?.GetStringColumnValues("string:Status") ?? Array.Empty().ToIArray(); - BimDocumentClientName = BimDocumentEntityTable?.GetStringColumnValues("string:ClientName") ?? Array.Empty().ToIArray(); - BimDocumentAddress = BimDocumentEntityTable?.GetStringColumnValues("string:Address") ?? Array.Empty().ToIArray(); - BimDocumentName = BimDocumentEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty().ToIArray(); - BimDocumentNumber = BimDocumentEntityTable?.GetStringColumnValues("string:Number") ?? Array.Empty().ToIArray(); - BimDocumentAuthor = BimDocumentEntityTable?.GetStringColumnValues("string:Author") ?? Array.Empty().ToIArray(); - BimDocumentBuildingName = BimDocumentEntityTable?.GetStringColumnValues("string:BuildingName") ?? Array.Empty().ToIArray(); - BimDocumentOrganizationName = BimDocumentEntityTable?.GetStringColumnValues("string:OrganizationName") ?? Array.Empty().ToIArray(); - BimDocumentOrganizationDescription = BimDocumentEntityTable?.GetStringColumnValues("string:OrganizationDescription") ?? Array.Empty().ToIArray(); - BimDocumentProduct = BimDocumentEntityTable?.GetStringColumnValues("string:Product") ?? Array.Empty().ToIArray(); - BimDocumentVersion = BimDocumentEntityTable?.GetStringColumnValues("string:Version") ?? Array.Empty().ToIArray(); - BimDocumentUser = BimDocumentEntityTable?.GetStringColumnValues("string:User") ?? Array.Empty().ToIArray(); - PhaseOrderInBimDocumentOrderIndex = PhaseOrderInBimDocumentEntityTable?.GetDataColumnValues("int:OrderIndex") ?? Array.Empty().ToIArray(); - CategoryName = CategoryEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty().ToIArray(); - CategoryId = (CategoryEntityTable?.GetDataColumnValues("long:Id") ?? CategoryEntityTable?.GetDataColumnValues("int:Id")?.Select(v => (Int64) v)) ?? Array.Empty().ToIArray(); - CategoryCategoryType = CategoryEntityTable?.GetStringColumnValues("string:CategoryType") ?? Array.Empty().ToIArray(); - CategoryLineColor_X = CategoryEntityTable?.GetDataColumnValues("double:LineColor.X") ?? Array.Empty().ToIArray(); - CategoryLineColor_Y = CategoryEntityTable?.GetDataColumnValues("double:LineColor.Y") ?? Array.Empty().ToIArray(); - CategoryLineColor_Z = CategoryEntityTable?.GetDataColumnValues("double:LineColor.Z") ?? Array.Empty().ToIArray(); - CategoryBuiltInCategory = CategoryEntityTable?.GetStringColumnValues("string:BuiltInCategory") ?? Array.Empty().ToIArray(); - FamilyStructuralMaterialType = FamilyEntityTable?.GetStringColumnValues("string:StructuralMaterialType") ?? Array.Empty().ToIArray(); - FamilyStructuralSectionShape = FamilyEntityTable?.GetStringColumnValues("string:StructuralSectionShape") ?? Array.Empty().ToIArray(); - FamilyIsSystemFamily = FamilyEntityTable?.GetDataColumnValues("byte:IsSystemFamily") ?? Array.Empty().ToIArray(); - FamilyIsInPlace = FamilyEntityTable?.GetDataColumnValues("byte:IsInPlace") ?? Array.Empty().ToIArray(); - FamilyTypeIsSystemFamilyType = FamilyTypeEntityTable?.GetDataColumnValues("byte:IsSystemFamilyType") ?? Array.Empty().ToIArray(); - FamilyInstanceFacingFlipped = FamilyInstanceEntityTable?.GetDataColumnValues("byte:FacingFlipped") ?? Array.Empty().ToIArray(); - FamilyInstanceFacingOrientation_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:FacingOrientation.X") ?? Array.Empty().ToIArray(); - FamilyInstanceFacingOrientation_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:FacingOrientation.Y") ?? Array.Empty().ToIArray(); - FamilyInstanceFacingOrientation_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:FacingOrientation.Z") ?? Array.Empty().ToIArray(); - FamilyInstanceHandFlipped = FamilyInstanceEntityTable?.GetDataColumnValues("byte:HandFlipped") ?? Array.Empty().ToIArray(); - FamilyInstanceMirrored = FamilyInstanceEntityTable?.GetDataColumnValues("byte:Mirrored") ?? Array.Empty().ToIArray(); - FamilyInstanceHasModifiedGeometry = FamilyInstanceEntityTable?.GetDataColumnValues("byte:HasModifiedGeometry") ?? Array.Empty().ToIArray(); - FamilyInstanceScale = FamilyInstanceEntityTable?.GetDataColumnValues("float:Scale") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisX_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisX.X") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisX_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisX.Y") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisX_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisX.Z") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisY_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisY.X") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisY_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisY.Y") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisY_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisY.Z") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisZ_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisZ.X") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisZ_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisZ.Y") ?? Array.Empty().ToIArray(); - FamilyInstanceBasisZ_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisZ.Z") ?? Array.Empty().ToIArray(); - FamilyInstanceTranslation_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:Translation.X") ?? Array.Empty().ToIArray(); - FamilyInstanceTranslation_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:Translation.Y") ?? Array.Empty().ToIArray(); - FamilyInstanceTranslation_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:Translation.Z") ?? Array.Empty().ToIArray(); - FamilyInstanceHandOrientation_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:HandOrientation.X") ?? Array.Empty().ToIArray(); - FamilyInstanceHandOrientation_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:HandOrientation.Y") ?? Array.Empty().ToIArray(); - FamilyInstanceHandOrientation_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:HandOrientation.Z") ?? Array.Empty().ToIArray(); - ViewTitle = ViewEntityTable?.GetStringColumnValues("string:Title") ?? Array.Empty().ToIArray(); - ViewViewType = ViewEntityTable?.GetStringColumnValues("string:ViewType") ?? Array.Empty().ToIArray(); - ViewUp_X = ViewEntityTable?.GetDataColumnValues("double:Up.X") ?? Array.Empty().ToIArray(); - ViewUp_Y = ViewEntityTable?.GetDataColumnValues("double:Up.Y") ?? Array.Empty().ToIArray(); - ViewUp_Z = ViewEntityTable?.GetDataColumnValues("double:Up.Z") ?? Array.Empty().ToIArray(); - ViewRight_X = ViewEntityTable?.GetDataColumnValues("double:Right.X") ?? Array.Empty().ToIArray(); - ViewRight_Y = ViewEntityTable?.GetDataColumnValues("double:Right.Y") ?? Array.Empty().ToIArray(); - ViewRight_Z = ViewEntityTable?.GetDataColumnValues("double:Right.Z") ?? Array.Empty().ToIArray(); - ViewOrigin_X = ViewEntityTable?.GetDataColumnValues("double:Origin.X") ?? Array.Empty().ToIArray(); - ViewOrigin_Y = ViewEntityTable?.GetDataColumnValues("double:Origin.Y") ?? Array.Empty().ToIArray(); - ViewOrigin_Z = ViewEntityTable?.GetDataColumnValues("double:Origin.Z") ?? Array.Empty().ToIArray(); - ViewViewDirection_X = ViewEntityTable?.GetDataColumnValues("double:ViewDirection.X") ?? Array.Empty().ToIArray(); - ViewViewDirection_Y = ViewEntityTable?.GetDataColumnValues("double:ViewDirection.Y") ?? Array.Empty().ToIArray(); - ViewViewDirection_Z = ViewEntityTable?.GetDataColumnValues("double:ViewDirection.Z") ?? Array.Empty().ToIArray(); - ViewViewPosition_X = ViewEntityTable?.GetDataColumnValues("double:ViewPosition.X") ?? Array.Empty().ToIArray(); - ViewViewPosition_Y = ViewEntityTable?.GetDataColumnValues("double:ViewPosition.Y") ?? Array.Empty().ToIArray(); - ViewViewPosition_Z = ViewEntityTable?.GetDataColumnValues("double:ViewPosition.Z") ?? Array.Empty().ToIArray(); - ViewScale = ViewEntityTable?.GetDataColumnValues("double:Scale") ?? Array.Empty().ToIArray(); - ViewOutline_Min_X = ViewEntityTable?.GetDataColumnValues("double:Outline.Min.X") ?? Array.Empty().ToIArray(); - ViewOutline_Min_Y = ViewEntityTable?.GetDataColumnValues("double:Outline.Min.Y") ?? Array.Empty().ToIArray(); - ViewOutline_Max_X = ViewEntityTable?.GetDataColumnValues("double:Outline.Max.X") ?? Array.Empty().ToIArray(); - ViewOutline_Max_Y = ViewEntityTable?.GetDataColumnValues("double:Outline.Max.Y") ?? Array.Empty().ToIArray(); - ViewDetailLevel = ViewEntityTable?.GetDataColumnValues("int:DetailLevel") ?? Array.Empty().ToIArray(); - LevelInViewExtents_Min_X = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Min.X") ?? Array.Empty().ToIArray(); - LevelInViewExtents_Min_Y = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Min.Y") ?? Array.Empty().ToIArray(); - LevelInViewExtents_Min_Z = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Min.Z") ?? Array.Empty().ToIArray(); - LevelInViewExtents_Max_X = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Max.X") ?? Array.Empty().ToIArray(); - LevelInViewExtents_Max_Y = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Max.Y") ?? Array.Empty().ToIArray(); - LevelInViewExtents_Max_Z = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Max.Z") ?? Array.Empty().ToIArray(); - CameraId = CameraEntityTable?.GetDataColumnValues("int:Id") ?? Array.Empty().ToIArray(); - CameraIsPerspective = CameraEntityTable?.GetDataColumnValues("int:IsPerspective") ?? Array.Empty().ToIArray(); - CameraVerticalExtent = CameraEntityTable?.GetDataColumnValues("double:VerticalExtent") ?? Array.Empty().ToIArray(); - CameraHorizontalExtent = CameraEntityTable?.GetDataColumnValues("double:HorizontalExtent") ?? Array.Empty().ToIArray(); - CameraFarDistance = CameraEntityTable?.GetDataColumnValues("double:FarDistance") ?? Array.Empty().ToIArray(); - CameraNearDistance = CameraEntityTable?.GetDataColumnValues("double:NearDistance") ?? Array.Empty().ToIArray(); - CameraTargetDistance = CameraEntityTable?.GetDataColumnValues("double:TargetDistance") ?? Array.Empty().ToIArray(); - CameraRightOffset = CameraEntityTable?.GetDataColumnValues("double:RightOffset") ?? Array.Empty().ToIArray(); - CameraUpOffset = CameraEntityTable?.GetDataColumnValues("double:UpOffset") ?? Array.Empty().ToIArray(); - MaterialName = MaterialEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty().ToIArray(); - MaterialMaterialCategory = MaterialEntityTable?.GetStringColumnValues("string:MaterialCategory") ?? Array.Empty().ToIArray(); - MaterialColor_X = MaterialEntityTable?.GetDataColumnValues("double:Color.X") ?? Array.Empty().ToIArray(); - MaterialColor_Y = MaterialEntityTable?.GetDataColumnValues("double:Color.Y") ?? Array.Empty().ToIArray(); - MaterialColor_Z = MaterialEntityTable?.GetDataColumnValues("double:Color.Z") ?? Array.Empty().ToIArray(); - MaterialColorUvScaling_X = MaterialEntityTable?.GetDataColumnValues("double:ColorUvScaling.X") ?? Array.Empty().ToIArray(); - MaterialColorUvScaling_Y = MaterialEntityTable?.GetDataColumnValues("double:ColorUvScaling.Y") ?? Array.Empty().ToIArray(); - MaterialColorUvOffset_X = MaterialEntityTable?.GetDataColumnValues("double:ColorUvOffset.X") ?? Array.Empty().ToIArray(); - MaterialColorUvOffset_Y = MaterialEntityTable?.GetDataColumnValues("double:ColorUvOffset.Y") ?? Array.Empty().ToIArray(); - MaterialNormalUvScaling_X = MaterialEntityTable?.GetDataColumnValues("double:NormalUvScaling.X") ?? Array.Empty().ToIArray(); - MaterialNormalUvScaling_Y = MaterialEntityTable?.GetDataColumnValues("double:NormalUvScaling.Y") ?? Array.Empty().ToIArray(); - MaterialNormalUvOffset_X = MaterialEntityTable?.GetDataColumnValues("double:NormalUvOffset.X") ?? Array.Empty().ToIArray(); - MaterialNormalUvOffset_Y = MaterialEntityTable?.GetDataColumnValues("double:NormalUvOffset.Y") ?? Array.Empty().ToIArray(); - MaterialNormalAmount = MaterialEntityTable?.GetDataColumnValues("double:NormalAmount") ?? Array.Empty().ToIArray(); - MaterialGlossiness = MaterialEntityTable?.GetDataColumnValues("double:Glossiness") ?? Array.Empty().ToIArray(); - MaterialSmoothness = MaterialEntityTable?.GetDataColumnValues("double:Smoothness") ?? Array.Empty().ToIArray(); - MaterialTransparency = MaterialEntityTable?.GetDataColumnValues("double:Transparency") ?? Array.Empty().ToIArray(); - MaterialInElementArea = MaterialInElementEntityTable?.GetDataColumnValues("double:Area") ?? Array.Empty().ToIArray(); - MaterialInElementVolume = MaterialInElementEntityTable?.GetDataColumnValues("double:Volume") ?? Array.Empty().ToIArray(); - MaterialInElementIsPaint = MaterialInElementEntityTable?.GetDataColumnValues("byte:IsPaint") ?? Array.Empty().ToIArray(); - CompoundStructureLayerOrderIndex = CompoundStructureLayerEntityTable?.GetDataColumnValues("int:OrderIndex") ?? Array.Empty().ToIArray(); - CompoundStructureLayerWidth = CompoundStructureLayerEntityTable?.GetDataColumnValues("double:Width") ?? Array.Empty().ToIArray(); - CompoundStructureLayerMaterialFunctionAssignment = CompoundStructureLayerEntityTable?.GetStringColumnValues("string:MaterialFunctionAssignment") ?? Array.Empty().ToIArray(); - CompoundStructureWidth = CompoundStructureEntityTable?.GetDataColumnValues("double:Width") ?? Array.Empty().ToIArray(); - GeometryBox_Min_X = GeometryEntityTable?.GetDataColumnValues("float:Box.Min.X") ?? Array.Empty().ToIArray(); - GeometryBox_Min_Y = GeometryEntityTable?.GetDataColumnValues("float:Box.Min.Y") ?? Array.Empty().ToIArray(); - GeometryBox_Min_Z = GeometryEntityTable?.GetDataColumnValues("float:Box.Min.Z") ?? Array.Empty().ToIArray(); - GeometryBox_Max_X = GeometryEntityTable?.GetDataColumnValues("float:Box.Max.X") ?? Array.Empty().ToIArray(); - GeometryBox_Max_Y = GeometryEntityTable?.GetDataColumnValues("float:Box.Max.Y") ?? Array.Empty().ToIArray(); - GeometryBox_Max_Z = GeometryEntityTable?.GetDataColumnValues("float:Box.Max.Z") ?? Array.Empty().ToIArray(); - GeometryVertexCount = GeometryEntityTable?.GetDataColumnValues("int:VertexCount") ?? Array.Empty().ToIArray(); - GeometryFaceCount = GeometryEntityTable?.GetDataColumnValues("int:FaceCount") ?? Array.Empty().ToIArray(); - SystemSystemType = SystemEntityTable?.GetDataColumnValues("int:SystemType") ?? Array.Empty().ToIArray(); - ElementInSystemRoles = ElementInSystemEntityTable?.GetDataColumnValues("int:Roles") ?? Array.Empty().ToIArray(); - WarningGuid = WarningEntityTable?.GetStringColumnValues("string:Guid") ?? Array.Empty().ToIArray(); - WarningSeverity = WarningEntityTable?.GetStringColumnValues("string:Severity") ?? Array.Empty().ToIArray(); - WarningDescription = WarningEntityTable?.GetStringColumnValues("string:Description") ?? Array.Empty().ToIArray(); - BasePointIsSurveyPoint = BasePointEntityTable?.GetDataColumnValues("byte:IsSurveyPoint") ?? Array.Empty().ToIArray(); - BasePointPosition_X = BasePointEntityTable?.GetDataColumnValues("double:Position.X") ?? Array.Empty().ToIArray(); - BasePointPosition_Y = BasePointEntityTable?.GetDataColumnValues("double:Position.Y") ?? Array.Empty().ToIArray(); - BasePointPosition_Z = BasePointEntityTable?.GetDataColumnValues("double:Position.Z") ?? Array.Empty().ToIArray(); - BasePointSharedPosition_X = BasePointEntityTable?.GetDataColumnValues("double:SharedPosition.X") ?? Array.Empty().ToIArray(); - BasePointSharedPosition_Y = BasePointEntityTable?.GetDataColumnValues("double:SharedPosition.Y") ?? Array.Empty().ToIArray(); - BasePointSharedPosition_Z = BasePointEntityTable?.GetDataColumnValues("double:SharedPosition.Z") ?? Array.Empty().ToIArray(); - PhaseFilterNew = PhaseFilterEntityTable?.GetDataColumnValues("int:New") ?? Array.Empty().ToIArray(); - PhaseFilterExisting = PhaseFilterEntityTable?.GetDataColumnValues("int:Existing") ?? Array.Empty().ToIArray(); - PhaseFilterDemolished = PhaseFilterEntityTable?.GetDataColumnValues("int:Demolished") ?? Array.Empty().ToIArray(); - PhaseFilterTemporary = PhaseFilterEntityTable?.GetDataColumnValues("int:Temporary") ?? Array.Empty().ToIArray(); - GridStartPoint_X = GridEntityTable?.GetDataColumnValues("double:StartPoint.X") ?? Array.Empty().ToIArray(); - GridStartPoint_Y = GridEntityTable?.GetDataColumnValues("double:StartPoint.Y") ?? Array.Empty().ToIArray(); - GridStartPoint_Z = GridEntityTable?.GetDataColumnValues("double:StartPoint.Z") ?? Array.Empty().ToIArray(); - GridEndPoint_X = GridEntityTable?.GetDataColumnValues("double:EndPoint.X") ?? Array.Empty().ToIArray(); - GridEndPoint_Y = GridEntityTable?.GetDataColumnValues("double:EndPoint.Y") ?? Array.Empty().ToIArray(); - GridEndPoint_Z = GridEntityTable?.GetDataColumnValues("double:EndPoint.Z") ?? Array.Empty().ToIArray(); - GridIsCurved = GridEntityTable?.GetDataColumnValues("byte:IsCurved") ?? Array.Empty().ToIArray(); - GridExtents_Min_X = GridEntityTable?.GetDataColumnValues("double:Extents.Min.X") ?? Array.Empty().ToIArray(); - GridExtents_Min_Y = GridEntityTable?.GetDataColumnValues("double:Extents.Min.Y") ?? Array.Empty().ToIArray(); - GridExtents_Min_Z = GridEntityTable?.GetDataColumnValues("double:Extents.Min.Z") ?? Array.Empty().ToIArray(); - GridExtents_Max_X = GridEntityTable?.GetDataColumnValues("double:Extents.Max.X") ?? Array.Empty().ToIArray(); - GridExtents_Max_Y = GridEntityTable?.GetDataColumnValues("double:Extents.Max.Y") ?? Array.Empty().ToIArray(); - GridExtents_Max_Z = GridEntityTable?.GetDataColumnValues("double:Extents.Max.Z") ?? Array.Empty().ToIArray(); - AreaValue = AreaEntityTable?.GetDataColumnValues("double:Value") ?? Array.Empty().ToIArray(); - AreaPerimeter = AreaEntityTable?.GetDataColumnValues("double:Perimeter") ?? Array.Empty().ToIArray(); - AreaNumber = AreaEntityTable?.GetStringColumnValues("string:Number") ?? Array.Empty().ToIArray(); - AreaIsGrossInterior = AreaEntityTable?.GetDataColumnValues("byte:IsGrossInterior") ?? Array.Empty().ToIArray(); - AreaSchemeIsGrossBuildingArea = AreaSchemeEntityTable?.GetDataColumnValues("byte:IsGrossBuildingArea") ?? Array.Empty().ToIArray(); - ScheduleColumnName = ScheduleColumnEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty().ToIArray(); - ScheduleColumnColumnIndex = ScheduleColumnEntityTable?.GetDataColumnValues("int:ColumnIndex") ?? Array.Empty().ToIArray(); - ScheduleCellValue = ScheduleCellEntityTable?.GetStringColumnValues("string:Value") ?? Array.Empty().ToIArray(); - ScheduleCellRowIndex = ScheduleCellEntityTable?.GetDataColumnValues("int:RowIndex") ?? Array.Empty().ToIArray(); - SiteLatitude = SiteEntityTable?.GetDataColumnValues("double:Latitude") ?? Array.Empty().ToIArray(); - SiteLongitude = SiteEntityTable?.GetDataColumnValues("double:Longitude") ?? Array.Empty().ToIArray(); - SiteAddress = SiteEntityTable?.GetStringColumnValues("string:Address") ?? Array.Empty().ToIArray(); - SiteElevation = SiteEntityTable?.GetDataColumnValues("double:Elevation") ?? Array.Empty().ToIArray(); - SiteNumber = SiteEntityTable?.GetStringColumnValues("string:Number") ?? Array.Empty().ToIArray(); - BuildingElevation = BuildingEntityTable?.GetDataColumnValues("double:Elevation") ?? Array.Empty().ToIArray(); - BuildingTerrainElevation = BuildingEntityTable?.GetDataColumnValues("double:TerrainElevation") ?? Array.Empty().ToIArray(); - BuildingAddress = BuildingEntityTable?.GetStringColumnValues("string:Address") ?? Array.Empty().ToIArray(); + AssetBufferName = AssetEntityTable?.GetStringColumnValues("string:BufferName") ?? Array.Empty(); + DisplayUnitSpec = DisplayUnitEntityTable?.GetStringColumnValues("string:Spec") ?? Array.Empty(); + DisplayUnitType = DisplayUnitEntityTable?.GetStringColumnValues("string:Type") ?? Array.Empty(); + DisplayUnitLabel = DisplayUnitEntityTable?.GetStringColumnValues("string:Label") ?? Array.Empty(); + ParameterDescriptorName = ParameterDescriptorEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty(); + ParameterDescriptorGroup = ParameterDescriptorEntityTable?.GetStringColumnValues("string:Group") ?? Array.Empty(); + ParameterDescriptorParameterType = ParameterDescriptorEntityTable?.GetStringColumnValues("string:ParameterType") ?? Array.Empty(); + ParameterDescriptorIsInstance = ParameterDescriptorEntityTable?.GetDataColumnValues("byte:IsInstance") ?? Array.Empty(); + ParameterDescriptorIsShared = ParameterDescriptorEntityTable?.GetDataColumnValues("byte:IsShared") ?? Array.Empty(); + ParameterDescriptorIsReadOnly = ParameterDescriptorEntityTable?.GetDataColumnValues("byte:IsReadOnly") ?? Array.Empty(); + ParameterDescriptorFlags = ParameterDescriptorEntityTable?.GetDataColumnValues("int:Flags") ?? Array.Empty(); + ParameterDescriptorGuid = ParameterDescriptorEntityTable?.GetStringColumnValues("string:Guid") ?? Array.Empty(); + ParameterDescriptorStorageType = ParameterDescriptorEntityTable?.GetDataColumnValues("int:StorageType") ?? Array.Empty(); + ParameterValue = ParameterEntityTable?.GetStringColumnValues("string:Value") ?? Array.Empty(); + ElementId = (ElementEntityTable?.GetDataColumnValues("long:Id") ?? ElementEntityTable?.GetDataColumnValues("int:Id")?.Select(v => (Int64) v)) ?? 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)) ?? Array.Empty(); + CategoryCategoryType = CategoryEntityTable?.GetStringColumnValues("string:CategoryType") ?? Array.Empty(); + CategoryLineColor_X = CategoryEntityTable?.GetDataColumnValues("double:LineColor.X") ?? Array.Empty(); + CategoryLineColor_Y = CategoryEntityTable?.GetDataColumnValues("double:LineColor.Y") ?? Array.Empty(); + CategoryLineColor_Z = CategoryEntityTable?.GetDataColumnValues("double:LineColor.Z") ?? Array.Empty(); + CategoryBuiltInCategory = CategoryEntityTable?.GetStringColumnValues("string:BuiltInCategory") ?? Array.Empty(); + FamilyStructuralMaterialType = FamilyEntityTable?.GetStringColumnValues("string:StructuralMaterialType") ?? Array.Empty(); + FamilyStructuralSectionShape = FamilyEntityTable?.GetStringColumnValues("string:StructuralSectionShape") ?? Array.Empty(); + FamilyIsSystemFamily = FamilyEntityTable?.GetDataColumnValues("byte:IsSystemFamily") ?? Array.Empty(); + FamilyIsInPlace = FamilyEntityTable?.GetDataColumnValues("byte:IsInPlace") ?? Array.Empty(); + FamilyTypeIsSystemFamilyType = FamilyTypeEntityTable?.GetDataColumnValues("byte:IsSystemFamilyType") ?? Array.Empty(); + FamilyInstanceFacingFlipped = FamilyInstanceEntityTable?.GetDataColumnValues("byte:FacingFlipped") ?? Array.Empty(); + FamilyInstanceFacingOrientation_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:FacingOrientation.X") ?? Array.Empty(); + FamilyInstanceFacingOrientation_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:FacingOrientation.Y") ?? Array.Empty(); + FamilyInstanceFacingOrientation_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:FacingOrientation.Z") ?? Array.Empty(); + FamilyInstanceHandFlipped = FamilyInstanceEntityTable?.GetDataColumnValues("byte:HandFlipped") ?? Array.Empty(); + FamilyInstanceMirrored = FamilyInstanceEntityTable?.GetDataColumnValues("byte:Mirrored") ?? Array.Empty(); + FamilyInstanceHasModifiedGeometry = FamilyInstanceEntityTable?.GetDataColumnValues("byte:HasModifiedGeometry") ?? Array.Empty(); + FamilyInstanceScale = FamilyInstanceEntityTable?.GetDataColumnValues("float:Scale") ?? Array.Empty(); + FamilyInstanceBasisX_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisX.X") ?? Array.Empty(); + FamilyInstanceBasisX_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisX.Y") ?? Array.Empty(); + FamilyInstanceBasisX_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisX.Z") ?? Array.Empty(); + FamilyInstanceBasisY_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisY.X") ?? Array.Empty(); + FamilyInstanceBasisY_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisY.Y") ?? Array.Empty(); + FamilyInstanceBasisY_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisY.Z") ?? Array.Empty(); + FamilyInstanceBasisZ_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisZ.X") ?? Array.Empty(); + FamilyInstanceBasisZ_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisZ.Y") ?? Array.Empty(); + FamilyInstanceBasisZ_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:BasisZ.Z") ?? Array.Empty(); + FamilyInstanceTranslation_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:Translation.X") ?? Array.Empty(); + FamilyInstanceTranslation_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:Translation.Y") ?? Array.Empty(); + FamilyInstanceTranslation_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:Translation.Z") ?? Array.Empty(); + FamilyInstanceHandOrientation_X = FamilyInstanceEntityTable?.GetDataColumnValues("float:HandOrientation.X") ?? Array.Empty(); + FamilyInstanceHandOrientation_Y = FamilyInstanceEntityTable?.GetDataColumnValues("float:HandOrientation.Y") ?? Array.Empty(); + FamilyInstanceHandOrientation_Z = FamilyInstanceEntityTable?.GetDataColumnValues("float:HandOrientation.Z") ?? Array.Empty(); + ViewTitle = ViewEntityTable?.GetStringColumnValues("string:Title") ?? Array.Empty(); + ViewViewType = ViewEntityTable?.GetStringColumnValues("string:ViewType") ?? Array.Empty(); + ViewUp_X = ViewEntityTable?.GetDataColumnValues("double:Up.X") ?? Array.Empty(); + ViewUp_Y = ViewEntityTable?.GetDataColumnValues("double:Up.Y") ?? Array.Empty(); + ViewUp_Z = ViewEntityTable?.GetDataColumnValues("double:Up.Z") ?? Array.Empty(); + ViewRight_X = ViewEntityTable?.GetDataColumnValues("double:Right.X") ?? Array.Empty(); + ViewRight_Y = ViewEntityTable?.GetDataColumnValues("double:Right.Y") ?? Array.Empty(); + ViewRight_Z = ViewEntityTable?.GetDataColumnValues("double:Right.Z") ?? Array.Empty(); + ViewOrigin_X = ViewEntityTable?.GetDataColumnValues("double:Origin.X") ?? Array.Empty(); + ViewOrigin_Y = ViewEntityTable?.GetDataColumnValues("double:Origin.Y") ?? Array.Empty(); + ViewOrigin_Z = ViewEntityTable?.GetDataColumnValues("double:Origin.Z") ?? Array.Empty(); + ViewViewDirection_X = ViewEntityTable?.GetDataColumnValues("double:ViewDirection.X") ?? Array.Empty(); + ViewViewDirection_Y = ViewEntityTable?.GetDataColumnValues("double:ViewDirection.Y") ?? Array.Empty(); + ViewViewDirection_Z = ViewEntityTable?.GetDataColumnValues("double:ViewDirection.Z") ?? Array.Empty(); + ViewViewPosition_X = ViewEntityTable?.GetDataColumnValues("double:ViewPosition.X") ?? Array.Empty(); + ViewViewPosition_Y = ViewEntityTable?.GetDataColumnValues("double:ViewPosition.Y") ?? Array.Empty(); + ViewViewPosition_Z = ViewEntityTable?.GetDataColumnValues("double:ViewPosition.Z") ?? Array.Empty(); + ViewScale = ViewEntityTable?.GetDataColumnValues("double:Scale") ?? Array.Empty(); + ViewOutline_Min_X = ViewEntityTable?.GetDataColumnValues("double:Outline.Min.X") ?? Array.Empty(); + ViewOutline_Min_Y = ViewEntityTable?.GetDataColumnValues("double:Outline.Min.Y") ?? Array.Empty(); + ViewOutline_Max_X = ViewEntityTable?.GetDataColumnValues("double:Outline.Max.X") ?? Array.Empty(); + ViewOutline_Max_Y = ViewEntityTable?.GetDataColumnValues("double:Outline.Max.Y") ?? Array.Empty(); + ViewDetailLevel = ViewEntityTable?.GetDataColumnValues("int:DetailLevel") ?? Array.Empty(); + LevelInViewExtents_Min_X = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Min.X") ?? Array.Empty(); + LevelInViewExtents_Min_Y = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Min.Y") ?? Array.Empty(); + LevelInViewExtents_Min_Z = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Min.Z") ?? Array.Empty(); + LevelInViewExtents_Max_X = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Max.X") ?? Array.Empty(); + LevelInViewExtents_Max_Y = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Max.Y") ?? Array.Empty(); + LevelInViewExtents_Max_Z = LevelInViewEntityTable?.GetDataColumnValues("double:Extents.Max.Z") ?? Array.Empty(); + CameraId = CameraEntityTable?.GetDataColumnValues("int:Id") ?? Array.Empty(); + CameraIsPerspective = CameraEntityTable?.GetDataColumnValues("int:IsPerspective") ?? Array.Empty(); + CameraVerticalExtent = CameraEntityTable?.GetDataColumnValues("double:VerticalExtent") ?? Array.Empty(); + CameraHorizontalExtent = CameraEntityTable?.GetDataColumnValues("double:HorizontalExtent") ?? Array.Empty(); + CameraFarDistance = CameraEntityTable?.GetDataColumnValues("double:FarDistance") ?? Array.Empty(); + CameraNearDistance = CameraEntityTable?.GetDataColumnValues("double:NearDistance") ?? Array.Empty(); + CameraTargetDistance = CameraEntityTable?.GetDataColumnValues("double:TargetDistance") ?? Array.Empty(); + CameraRightOffset = CameraEntityTable?.GetDataColumnValues("double:RightOffset") ?? Array.Empty(); + CameraUpOffset = CameraEntityTable?.GetDataColumnValues("double:UpOffset") ?? Array.Empty(); + MaterialName = MaterialEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty(); + MaterialMaterialCategory = MaterialEntityTable?.GetStringColumnValues("string:MaterialCategory") ?? Array.Empty(); + MaterialColor_X = MaterialEntityTable?.GetDataColumnValues("double:Color.X") ?? Array.Empty(); + MaterialColor_Y = MaterialEntityTable?.GetDataColumnValues("double:Color.Y") ?? Array.Empty(); + MaterialColor_Z = MaterialEntityTable?.GetDataColumnValues("double:Color.Z") ?? Array.Empty(); + MaterialColorUvScaling_X = MaterialEntityTable?.GetDataColumnValues("double:ColorUvScaling.X") ?? Array.Empty(); + MaterialColorUvScaling_Y = MaterialEntityTable?.GetDataColumnValues("double:ColorUvScaling.Y") ?? Array.Empty(); + MaterialColorUvOffset_X = MaterialEntityTable?.GetDataColumnValues("double:ColorUvOffset.X") ?? Array.Empty(); + MaterialColorUvOffset_Y = MaterialEntityTable?.GetDataColumnValues("double:ColorUvOffset.Y") ?? Array.Empty(); + MaterialNormalUvScaling_X = MaterialEntityTable?.GetDataColumnValues("double:NormalUvScaling.X") ?? Array.Empty(); + MaterialNormalUvScaling_Y = MaterialEntityTable?.GetDataColumnValues("double:NormalUvScaling.Y") ?? Array.Empty(); + MaterialNormalUvOffset_X = MaterialEntityTable?.GetDataColumnValues("double:NormalUvOffset.X") ?? Array.Empty(); + MaterialNormalUvOffset_Y = MaterialEntityTable?.GetDataColumnValues("double:NormalUvOffset.Y") ?? Array.Empty(); + MaterialNormalAmount = MaterialEntityTable?.GetDataColumnValues("double:NormalAmount") ?? Array.Empty(); + MaterialGlossiness = MaterialEntityTable?.GetDataColumnValues("double:Glossiness") ?? Array.Empty(); + MaterialSmoothness = MaterialEntityTable?.GetDataColumnValues("double:Smoothness") ?? Array.Empty(); + MaterialTransparency = MaterialEntityTable?.GetDataColumnValues("double:Transparency") ?? Array.Empty(); + MaterialInElementArea = MaterialInElementEntityTable?.GetDataColumnValues("double:Area") ?? Array.Empty(); + MaterialInElementVolume = MaterialInElementEntityTable?.GetDataColumnValues("double:Volume") ?? Array.Empty(); + MaterialInElementIsPaint = MaterialInElementEntityTable?.GetDataColumnValues("byte:IsPaint") ?? Array.Empty(); + CompoundStructureLayerOrderIndex = CompoundStructureLayerEntityTable?.GetDataColumnValues("int:OrderIndex") ?? Array.Empty(); + CompoundStructureLayerWidth = CompoundStructureLayerEntityTable?.GetDataColumnValues("double:Width") ?? Array.Empty(); + CompoundStructureLayerMaterialFunctionAssignment = CompoundStructureLayerEntityTable?.GetStringColumnValues("string:MaterialFunctionAssignment") ?? Array.Empty(); + CompoundStructureWidth = CompoundStructureEntityTable?.GetDataColumnValues("double:Width") ?? Array.Empty(); + GeometryBox_Min_X = GeometryEntityTable?.GetDataColumnValues("float:Box.Min.X") ?? Array.Empty(); + GeometryBox_Min_Y = GeometryEntityTable?.GetDataColumnValues("float:Box.Min.Y") ?? Array.Empty(); + GeometryBox_Min_Z = GeometryEntityTable?.GetDataColumnValues("float:Box.Min.Z") ?? Array.Empty(); + GeometryBox_Max_X = GeometryEntityTable?.GetDataColumnValues("float:Box.Max.X") ?? Array.Empty(); + GeometryBox_Max_Y = GeometryEntityTable?.GetDataColumnValues("float:Box.Max.Y") ?? Array.Empty(); + GeometryBox_Max_Z = GeometryEntityTable?.GetDataColumnValues("float:Box.Max.Z") ?? Array.Empty(); + GeometryVertexCount = GeometryEntityTable?.GetDataColumnValues("int:VertexCount") ?? Array.Empty(); + GeometryFaceCount = GeometryEntityTable?.GetDataColumnValues("int:FaceCount") ?? Array.Empty(); + SystemSystemType = SystemEntityTable?.GetDataColumnValues("int:SystemType") ?? Array.Empty(); + ElementInSystemRoles = ElementInSystemEntityTable?.GetDataColumnValues("int:Roles") ?? Array.Empty(); + WarningGuid = WarningEntityTable?.GetStringColumnValues("string:Guid") ?? Array.Empty(); + WarningSeverity = WarningEntityTable?.GetStringColumnValues("string:Severity") ?? Array.Empty(); + WarningDescription = WarningEntityTable?.GetStringColumnValues("string:Description") ?? Array.Empty(); + BasePointIsSurveyPoint = BasePointEntityTable?.GetDataColumnValues("byte:IsSurveyPoint") ?? Array.Empty(); + BasePointPosition_X = BasePointEntityTable?.GetDataColumnValues("double:Position.X") ?? Array.Empty(); + BasePointPosition_Y = BasePointEntityTable?.GetDataColumnValues("double:Position.Y") ?? Array.Empty(); + BasePointPosition_Z = BasePointEntityTable?.GetDataColumnValues("double:Position.Z") ?? Array.Empty(); + BasePointSharedPosition_X = BasePointEntityTable?.GetDataColumnValues("double:SharedPosition.X") ?? Array.Empty(); + BasePointSharedPosition_Y = BasePointEntityTable?.GetDataColumnValues("double:SharedPosition.Y") ?? Array.Empty(); + BasePointSharedPosition_Z = BasePointEntityTable?.GetDataColumnValues("double:SharedPosition.Z") ?? Array.Empty(); + PhaseFilterNew = PhaseFilterEntityTable?.GetDataColumnValues("int:New") ?? Array.Empty(); + PhaseFilterExisting = PhaseFilterEntityTable?.GetDataColumnValues("int:Existing") ?? Array.Empty(); + PhaseFilterDemolished = PhaseFilterEntityTable?.GetDataColumnValues("int:Demolished") ?? Array.Empty(); + PhaseFilterTemporary = PhaseFilterEntityTable?.GetDataColumnValues("int:Temporary") ?? Array.Empty(); + GridStartPoint_X = GridEntityTable?.GetDataColumnValues("double:StartPoint.X") ?? Array.Empty(); + GridStartPoint_Y = GridEntityTable?.GetDataColumnValues("double:StartPoint.Y") ?? Array.Empty(); + GridStartPoint_Z = GridEntityTable?.GetDataColumnValues("double:StartPoint.Z") ?? Array.Empty(); + GridEndPoint_X = GridEntityTable?.GetDataColumnValues("double:EndPoint.X") ?? Array.Empty(); + GridEndPoint_Y = GridEntityTable?.GetDataColumnValues("double:EndPoint.Y") ?? Array.Empty(); + GridEndPoint_Z = GridEntityTable?.GetDataColumnValues("double:EndPoint.Z") ?? Array.Empty(); + GridIsCurved = GridEntityTable?.GetDataColumnValues("byte:IsCurved") ?? Array.Empty(); + GridExtents_Min_X = GridEntityTable?.GetDataColumnValues("double:Extents.Min.X") ?? Array.Empty(); + GridExtents_Min_Y = GridEntityTable?.GetDataColumnValues("double:Extents.Min.Y") ?? Array.Empty(); + GridExtents_Min_Z = GridEntityTable?.GetDataColumnValues("double:Extents.Min.Z") ?? Array.Empty(); + GridExtents_Max_X = GridEntityTable?.GetDataColumnValues("double:Extents.Max.X") ?? Array.Empty(); + GridExtents_Max_Y = GridEntityTable?.GetDataColumnValues("double:Extents.Max.Y") ?? Array.Empty(); + GridExtents_Max_Z = GridEntityTable?.GetDataColumnValues("double:Extents.Max.Z") ?? Array.Empty(); + AreaValue = AreaEntityTable?.GetDataColumnValues("double:Value") ?? Array.Empty(); + AreaPerimeter = AreaEntityTable?.GetDataColumnValues("double:Perimeter") ?? Array.Empty(); + AreaNumber = AreaEntityTable?.GetStringColumnValues("string:Number") ?? Array.Empty(); + AreaIsGrossInterior = AreaEntityTable?.GetDataColumnValues("byte:IsGrossInterior") ?? Array.Empty(); + AreaSchemeIsGrossBuildingArea = AreaSchemeEntityTable?.GetDataColumnValues("byte:IsGrossBuildingArea") ?? Array.Empty(); + ScheduleColumnName = ScheduleColumnEntityTable?.GetStringColumnValues("string:Name") ?? Array.Empty(); + ScheduleColumnColumnIndex = ScheduleColumnEntityTable?.GetDataColumnValues("int:ColumnIndex") ?? Array.Empty(); + ScheduleCellValue = ScheduleCellEntityTable?.GetStringColumnValues("string:Value") ?? Array.Empty(); + ScheduleCellRowIndex = ScheduleCellEntityTable?.GetDataColumnValues("int:RowIndex") ?? Array.Empty(); + SiteLatitude = SiteEntityTable?.GetDataColumnValues("double:Latitude") ?? Array.Empty(); + SiteLongitude = SiteEntityTable?.GetDataColumnValues("double:Longitude") ?? Array.Empty(); + SiteAddress = SiteEntityTable?.GetStringColumnValues("string:Address") ?? Array.Empty(); + SiteElevation = SiteEntityTable?.GetDataColumnValues("double:Elevation") ?? Array.Empty(); + SiteNumber = SiteEntityTable?.GetStringColumnValues("string:Number") ?? Array.Empty(); + BuildingElevation = BuildingEntityTable?.GetDataColumnValues("double:Elevation") ?? Array.Empty(); + BuildingTerrainElevation = BuildingEntityTable?.GetDataColumnValues("double:TerrainElevation") ?? Array.Empty(); + BuildingAddress = BuildingEntityTable?.GetStringColumnValues("string:Address") ?? Array.Empty(); // Initialize entity relational columns - ParameterDescriptorDisplayUnitIndex = ParameterDescriptorEntityTable?.GetIndexColumnValues("index:Vim.DisplayUnit:DisplayUnit") ?? Array.Empty().ToIArray(); - ParameterParameterDescriptorIndex = ParameterEntityTable?.GetIndexColumnValues("index:Vim.ParameterDescriptor:ParameterDescriptor") ?? Array.Empty().ToIArray(); - ParameterElementIndex = ParameterEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ElementLevelIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Level:Level") ?? Array.Empty().ToIArray(); - ElementPhaseCreatedIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Phase:PhaseCreated") ?? Array.Empty().ToIArray(); - ElementPhaseDemolishedIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Phase:PhaseDemolished") ?? Array.Empty().ToIArray(); - ElementCategoryIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Category:Category") ?? Array.Empty().ToIArray(); - ElementWorksetIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Workset:Workset") ?? Array.Empty().ToIArray(); - ElementDesignOptionIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.DesignOption:DesignOption") ?? Array.Empty().ToIArray(); - ElementOwnerViewIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.View:OwnerView") ?? Array.Empty().ToIArray(); - ElementGroupIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Group:Group") ?? Array.Empty().ToIArray(); - ElementAssemblyInstanceIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.AssemblyInstance:AssemblyInstance") ?? Array.Empty().ToIArray(); - ElementBimDocumentIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty().ToIArray(); - ElementRoomIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Room:Room") ?? Array.Empty().ToIArray(); - WorksetBimDocumentIndex = WorksetEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty().ToIArray(); - AssemblyInstanceElementIndex = AssemblyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - GroupElementIndex = GroupEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - DesignOptionElementIndex = DesignOptionEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - LevelFamilyTypeIndex = LevelEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty().ToIArray(); - LevelBuildingIndex = LevelEntityTable?.GetIndexColumnValues("index:Vim.Building:Building") ?? Array.Empty().ToIArray(); - LevelElementIndex = LevelEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - PhaseElementIndex = PhaseEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - RoomUpperLimitIndex = RoomEntityTable?.GetIndexColumnValues("index:Vim.Level:UpperLimit") ?? Array.Empty().ToIArray(); - RoomElementIndex = RoomEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - BimDocumentActiveViewIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.View:ActiveView") ?? Array.Empty().ToIArray(); - BimDocumentOwnerFamilyIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.Family:OwnerFamily") ?? Array.Empty().ToIArray(); - BimDocumentParentIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:Parent") ?? Array.Empty().ToIArray(); - BimDocumentElementIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - DisplayUnitInBimDocumentDisplayUnitIndex = DisplayUnitInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.DisplayUnit:DisplayUnit") ?? Array.Empty().ToIArray(); - DisplayUnitInBimDocumentBimDocumentIndex = DisplayUnitInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty().ToIArray(); - PhaseOrderInBimDocumentPhaseIndex = PhaseOrderInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.Phase:Phase") ?? Array.Empty().ToIArray(); - PhaseOrderInBimDocumentBimDocumentIndex = PhaseOrderInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty().ToIArray(); - CategoryParentIndex = CategoryEntityTable?.GetIndexColumnValues("index:Vim.Category:Parent") ?? Array.Empty().ToIArray(); - CategoryMaterialIndex = CategoryEntityTable?.GetIndexColumnValues("index:Vim.Material:Material") ?? Array.Empty().ToIArray(); - FamilyFamilyCategoryIndex = FamilyEntityTable?.GetIndexColumnValues("index:Vim.Category:FamilyCategory") ?? Array.Empty().ToIArray(); - FamilyElementIndex = FamilyEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - FamilyTypeFamilyIndex = FamilyTypeEntityTable?.GetIndexColumnValues("index:Vim.Family:Family") ?? Array.Empty().ToIArray(); - FamilyTypeCompoundStructureIndex = FamilyTypeEntityTable?.GetIndexColumnValues("index:Vim.CompoundStructure:CompoundStructure") ?? Array.Empty().ToIArray(); - FamilyTypeElementIndex = FamilyTypeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - FamilyInstanceFamilyTypeIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty().ToIArray(); - FamilyInstanceHostIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:Host") ?? Array.Empty().ToIArray(); - FamilyInstanceFromRoomIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Room:FromRoom") ?? Array.Empty().ToIArray(); - FamilyInstanceToRoomIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Room:ToRoom") ?? Array.Empty().ToIArray(); - FamilyInstanceSuperComponentIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:SuperComponent") ?? Array.Empty().ToIArray(); - FamilyInstanceElementIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ViewCameraIndex = ViewEntityTable?.GetIndexColumnValues("index:Vim.Camera:Camera") ?? Array.Empty().ToIArray(); - ViewFamilyTypeIndex = ViewEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty().ToIArray(); - ViewElementIndex = ViewEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ElementInViewViewIndex = ElementInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty().ToIArray(); - ElementInViewElementIndex = ElementInViewEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ShapeInViewShapeIndex = ShapeInViewEntityTable?.GetIndexColumnValues("index:Vim.Shape:Shape") ?? Array.Empty().ToIArray(); - ShapeInViewViewIndex = ShapeInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty().ToIArray(); - AssetInViewAssetIndex = AssetInViewEntityTable?.GetIndexColumnValues("index:Vim.Asset:Asset") ?? Array.Empty().ToIArray(); - AssetInViewViewIndex = AssetInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty().ToIArray(); - AssetInViewSheetAssetIndex = AssetInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.Asset:Asset") ?? Array.Empty().ToIArray(); - AssetInViewSheetViewSheetIndex = AssetInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheet:ViewSheet") ?? Array.Empty().ToIArray(); - LevelInViewLevelIndex = LevelInViewEntityTable?.GetIndexColumnValues("index:Vim.Level:Level") ?? Array.Empty().ToIArray(); - LevelInViewViewIndex = LevelInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty().ToIArray(); - MaterialColorTextureFileIndex = MaterialEntityTable?.GetIndexColumnValues("index:Vim.Asset:ColorTextureFile") ?? Array.Empty().ToIArray(); - MaterialNormalTextureFileIndex = MaterialEntityTable?.GetIndexColumnValues("index:Vim.Asset:NormalTextureFile") ?? Array.Empty().ToIArray(); - MaterialElementIndex = MaterialEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - MaterialInElementMaterialIndex = MaterialInElementEntityTable?.GetIndexColumnValues("index:Vim.Material:Material") ?? Array.Empty().ToIArray(); - MaterialInElementElementIndex = MaterialInElementEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - CompoundStructureLayerMaterialIndex = CompoundStructureLayerEntityTable?.GetIndexColumnValues("index:Vim.Material:Material") ?? Array.Empty().ToIArray(); - CompoundStructureLayerCompoundStructureIndex = CompoundStructureLayerEntityTable?.GetIndexColumnValues("index:Vim.CompoundStructure:CompoundStructure") ?? Array.Empty().ToIArray(); - CompoundStructureStructuralLayerIndex = CompoundStructureEntityTable?.GetIndexColumnValues("index:Vim.CompoundStructureLayer:StructuralLayer") ?? Array.Empty().ToIArray(); - NodeElementIndex = NodeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ShapeElementIndex = ShapeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ShapeCollectionElementIndex = ShapeCollectionEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ShapeInShapeCollectionShapeIndex = ShapeInShapeCollectionEntityTable?.GetIndexColumnValues("index:Vim.Shape:Shape") ?? Array.Empty().ToIArray(); - ShapeInShapeCollectionShapeCollectionIndex = ShapeInShapeCollectionEntityTable?.GetIndexColumnValues("index:Vim.ShapeCollection:ShapeCollection") ?? Array.Empty().ToIArray(); - SystemFamilyTypeIndex = SystemEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty().ToIArray(); - SystemElementIndex = SystemEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ElementInSystemSystemIndex = ElementInSystemEntityTable?.GetIndexColumnValues("index:Vim.System:System") ?? Array.Empty().ToIArray(); - ElementInSystemElementIndex = ElementInSystemEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - WarningBimDocumentIndex = WarningEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty().ToIArray(); - ElementInWarningWarningIndex = ElementInWarningEntityTable?.GetIndexColumnValues("index:Vim.Warning:Warning") ?? Array.Empty().ToIArray(); - ElementInWarningElementIndex = ElementInWarningEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - BasePointElementIndex = BasePointEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - PhaseFilterElementIndex = PhaseFilterEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - GridFamilyTypeIndex = GridEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty().ToIArray(); - GridElementIndex = GridEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - AreaAreaSchemeIndex = AreaEntityTable?.GetIndexColumnValues("index:Vim.AreaScheme:AreaScheme") ?? Array.Empty().ToIArray(); - AreaElementIndex = AreaEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - AreaSchemeElementIndex = AreaSchemeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ScheduleElementIndex = ScheduleEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ScheduleColumnScheduleIndex = ScheduleColumnEntityTable?.GetIndexColumnValues("index:Vim.Schedule:Schedule") ?? Array.Empty().ToIArray(); - ScheduleCellScheduleColumnIndex = ScheduleCellEntityTable?.GetIndexColumnValues("index:Vim.ScheduleColumn:ScheduleColumn") ?? Array.Empty().ToIArray(); - ViewSheetSetElementIndex = ViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ViewSheetFamilyTypeIndex = ViewSheetEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty().ToIArray(); - ViewSheetElementIndex = ViewSheetEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - ViewSheetInViewSheetSetViewSheetIndex = ViewSheetInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheet:ViewSheet") ?? Array.Empty().ToIArray(); - ViewSheetInViewSheetSetViewSheetSetIndex = ViewSheetInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheetSet:ViewSheetSet") ?? Array.Empty().ToIArray(); - ViewInViewSheetSetViewIndex = ViewInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty().ToIArray(); - ViewInViewSheetSetViewSheetSetIndex = ViewInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheetSet:ViewSheetSet") ?? Array.Empty().ToIArray(); - ViewInViewSheetViewIndex = ViewInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty().ToIArray(); - ViewInViewSheetViewSheetIndex = ViewInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheet:ViewSheet") ?? Array.Empty().ToIArray(); - SiteElementIndex = SiteEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); - BuildingSiteIndex = BuildingEntityTable?.GetIndexColumnValues("index:Vim.Site:Site") ?? Array.Empty().ToIArray(); - BuildingElementIndex = BuildingEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty().ToIArray(); + ParameterDescriptorDisplayUnitIndex = ParameterDescriptorEntityTable?.GetIndexColumnValues("index:Vim.DisplayUnit:DisplayUnit") ?? Array.Empty(); + ParameterParameterDescriptorIndex = ParameterEntityTable?.GetIndexColumnValues("index:Vim.ParameterDescriptor:ParameterDescriptor") ?? Array.Empty(); + ParameterElementIndex = ParameterEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ElementLevelIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Level:Level") ?? Array.Empty(); + ElementPhaseCreatedIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Phase:PhaseCreated") ?? Array.Empty(); + ElementPhaseDemolishedIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Phase:PhaseDemolished") ?? Array.Empty(); + ElementCategoryIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Category:Category") ?? Array.Empty(); + ElementWorksetIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Workset:Workset") ?? Array.Empty(); + ElementDesignOptionIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.DesignOption:DesignOption") ?? Array.Empty(); + ElementOwnerViewIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.View:OwnerView") ?? Array.Empty(); + ElementGroupIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Group:Group") ?? Array.Empty(); + ElementAssemblyInstanceIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.AssemblyInstance:AssemblyInstance") ?? Array.Empty(); + ElementBimDocumentIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty(); + ElementRoomIndex = ElementEntityTable?.GetIndexColumnValues("index:Vim.Room:Room") ?? Array.Empty(); + WorksetBimDocumentIndex = WorksetEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty(); + AssemblyInstanceElementIndex = AssemblyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + GroupElementIndex = GroupEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + DesignOptionElementIndex = DesignOptionEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + LevelFamilyTypeIndex = LevelEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty(); + LevelBuildingIndex = LevelEntityTable?.GetIndexColumnValues("index:Vim.Building:Building") ?? Array.Empty(); + LevelElementIndex = LevelEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + PhaseElementIndex = PhaseEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + RoomUpperLimitIndex = RoomEntityTable?.GetIndexColumnValues("index:Vim.Level:UpperLimit") ?? Array.Empty(); + RoomElementIndex = RoomEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + BimDocumentActiveViewIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.View:ActiveView") ?? Array.Empty(); + BimDocumentOwnerFamilyIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.Family:OwnerFamily") ?? Array.Empty(); + BimDocumentParentIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:Parent") ?? Array.Empty(); + BimDocumentElementIndex = BimDocumentEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + DisplayUnitInBimDocumentDisplayUnitIndex = DisplayUnitInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.DisplayUnit:DisplayUnit") ?? Array.Empty(); + DisplayUnitInBimDocumentBimDocumentIndex = DisplayUnitInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty(); + PhaseOrderInBimDocumentPhaseIndex = PhaseOrderInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.Phase:Phase") ?? Array.Empty(); + PhaseOrderInBimDocumentBimDocumentIndex = PhaseOrderInBimDocumentEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty(); + CategoryParentIndex = CategoryEntityTable?.GetIndexColumnValues("index:Vim.Category:Parent") ?? Array.Empty(); + CategoryMaterialIndex = CategoryEntityTable?.GetIndexColumnValues("index:Vim.Material:Material") ?? Array.Empty(); + FamilyFamilyCategoryIndex = FamilyEntityTable?.GetIndexColumnValues("index:Vim.Category:FamilyCategory") ?? Array.Empty(); + FamilyElementIndex = FamilyEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + FamilyTypeFamilyIndex = FamilyTypeEntityTable?.GetIndexColumnValues("index:Vim.Family:Family") ?? Array.Empty(); + FamilyTypeCompoundStructureIndex = FamilyTypeEntityTable?.GetIndexColumnValues("index:Vim.CompoundStructure:CompoundStructure") ?? Array.Empty(); + FamilyTypeElementIndex = FamilyTypeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + FamilyInstanceFamilyTypeIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty(); + FamilyInstanceHostIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:Host") ?? Array.Empty(); + FamilyInstanceFromRoomIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Room:FromRoom") ?? Array.Empty(); + FamilyInstanceToRoomIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Room:ToRoom") ?? Array.Empty(); + FamilyInstanceSuperComponentIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:SuperComponent") ?? Array.Empty(); + FamilyInstanceElementIndex = FamilyInstanceEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ViewCameraIndex = ViewEntityTable?.GetIndexColumnValues("index:Vim.Camera:Camera") ?? Array.Empty(); + ViewFamilyTypeIndex = ViewEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty(); + ViewElementIndex = ViewEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ElementInViewViewIndex = ElementInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty(); + ElementInViewElementIndex = ElementInViewEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ShapeInViewShapeIndex = ShapeInViewEntityTable?.GetIndexColumnValues("index:Vim.Shape:Shape") ?? Array.Empty(); + ShapeInViewViewIndex = ShapeInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty(); + AssetInViewAssetIndex = AssetInViewEntityTable?.GetIndexColumnValues("index:Vim.Asset:Asset") ?? Array.Empty(); + AssetInViewViewIndex = AssetInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty(); + AssetInViewSheetAssetIndex = AssetInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.Asset:Asset") ?? Array.Empty(); + AssetInViewSheetViewSheetIndex = AssetInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheet:ViewSheet") ?? Array.Empty(); + LevelInViewLevelIndex = LevelInViewEntityTable?.GetIndexColumnValues("index:Vim.Level:Level") ?? Array.Empty(); + LevelInViewViewIndex = LevelInViewEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty(); + MaterialColorTextureFileIndex = MaterialEntityTable?.GetIndexColumnValues("index:Vim.Asset:ColorTextureFile") ?? Array.Empty(); + MaterialNormalTextureFileIndex = MaterialEntityTable?.GetIndexColumnValues("index:Vim.Asset:NormalTextureFile") ?? Array.Empty(); + MaterialElementIndex = MaterialEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + MaterialInElementMaterialIndex = MaterialInElementEntityTable?.GetIndexColumnValues("index:Vim.Material:Material") ?? Array.Empty(); + MaterialInElementElementIndex = MaterialInElementEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + CompoundStructureLayerMaterialIndex = CompoundStructureLayerEntityTable?.GetIndexColumnValues("index:Vim.Material:Material") ?? Array.Empty(); + CompoundStructureLayerCompoundStructureIndex = CompoundStructureLayerEntityTable?.GetIndexColumnValues("index:Vim.CompoundStructure:CompoundStructure") ?? Array.Empty(); + CompoundStructureStructuralLayerIndex = CompoundStructureEntityTable?.GetIndexColumnValues("index:Vim.CompoundStructureLayer:StructuralLayer") ?? Array.Empty(); + NodeElementIndex = NodeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ShapeElementIndex = ShapeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ShapeCollectionElementIndex = ShapeCollectionEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ShapeInShapeCollectionShapeIndex = ShapeInShapeCollectionEntityTable?.GetIndexColumnValues("index:Vim.Shape:Shape") ?? Array.Empty(); + ShapeInShapeCollectionShapeCollectionIndex = ShapeInShapeCollectionEntityTable?.GetIndexColumnValues("index:Vim.ShapeCollection:ShapeCollection") ?? Array.Empty(); + SystemFamilyTypeIndex = SystemEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty(); + SystemElementIndex = SystemEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ElementInSystemSystemIndex = ElementInSystemEntityTable?.GetIndexColumnValues("index:Vim.System:System") ?? Array.Empty(); + ElementInSystemElementIndex = ElementInSystemEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + WarningBimDocumentIndex = WarningEntityTable?.GetIndexColumnValues("index:Vim.BimDocument:BimDocument") ?? Array.Empty(); + ElementInWarningWarningIndex = ElementInWarningEntityTable?.GetIndexColumnValues("index:Vim.Warning:Warning") ?? Array.Empty(); + ElementInWarningElementIndex = ElementInWarningEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + BasePointElementIndex = BasePointEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + PhaseFilterElementIndex = PhaseFilterEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + GridFamilyTypeIndex = GridEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty(); + GridElementIndex = GridEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + AreaAreaSchemeIndex = AreaEntityTable?.GetIndexColumnValues("index:Vim.AreaScheme:AreaScheme") ?? Array.Empty(); + AreaElementIndex = AreaEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + AreaSchemeElementIndex = AreaSchemeEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ScheduleElementIndex = ScheduleEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ScheduleColumnScheduleIndex = ScheduleColumnEntityTable?.GetIndexColumnValues("index:Vim.Schedule:Schedule") ?? Array.Empty(); + ScheduleCellScheduleColumnIndex = ScheduleCellEntityTable?.GetIndexColumnValues("index:Vim.ScheduleColumn:ScheduleColumn") ?? Array.Empty(); + ViewSheetSetElementIndex = ViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ViewSheetFamilyTypeIndex = ViewSheetEntityTable?.GetIndexColumnValues("index:Vim.FamilyType:FamilyType") ?? Array.Empty(); + ViewSheetElementIndex = ViewSheetEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + ViewSheetInViewSheetSetViewSheetIndex = ViewSheetInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheet:ViewSheet") ?? Array.Empty(); + ViewSheetInViewSheetSetViewSheetSetIndex = ViewSheetInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheetSet:ViewSheetSet") ?? Array.Empty(); + ViewInViewSheetSetViewIndex = ViewInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty(); + ViewInViewSheetSetViewSheetSetIndex = ViewInViewSheetSetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheetSet:ViewSheetSet") ?? Array.Empty(); + ViewInViewSheetViewIndex = ViewInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.View:View") ?? Array.Empty(); + ViewInViewSheetViewSheetIndex = ViewInViewSheetEntityTable?.GetIndexColumnValues("index:Vim.ViewSheet:ViewSheet") ?? Array.Empty(); + SiteElementIndex = SiteEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); + BuildingSiteIndex = BuildingEntityTable?.GetIndexColumnValues("index:Vim.Site:Site") ?? Array.Empty(); + BuildingElementIndex = BuildingEntityTable?.GetIndexColumnValues("index:Vim.Element:Element") ?? Array.Empty(); // Initialize entity collections AssetList = NumAsset.Select(i => GetAsset(i)); diff --git a/src/cs/vim/Vim.Format/ObjectModel/Validation.cs b/src/cs/vim/Vim.Format/ObjectModel/Validation.cs index 24fb6399..e2a6afec 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/Validation.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/Validation.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Vim.LinqArray; using Vim.Math3d; +using Vim.G3d; namespace Vim.Format.ObjectModel { @@ -143,7 +144,7 @@ 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}'"); } } @@ -253,7 +254,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); @@ -316,7 +317,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/Validation.cs b/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs index 8914d0f0..55be0d2e 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -7,7 +8,6 @@ using Vim.Format.Geometry; using Vim.Format.ObjectModel; using Vim.Util; -using Vim.LinqArray; using Vim.Math3d; namespace Vim.Format.SceneBuilder @@ -30,7 +30,7 @@ private static void ValidateGeometry(this VimScene vim) vim.Document.Geometry.ToIMesh().Validate(); // Validate the individual meshes. - foreach (var g in vim.Meshes.ToEnumerable()) + foreach (var g in vim.Meshes) g.Validate(); } @@ -56,7 +56,8 @@ private 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 IList arr) { var numEntities = arr.Count; @@ -170,7 +171,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 039add74..c4d25b89 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -7,7 +8,6 @@ using Vim.Format.ObjectModel; using Vim.Util; using Vim.G3d; -using Vim.LinqArray; using Vim.Math3d; using IVimSceneProgress = System.IProgress<(string, double)>; using Vim.BFastLib; @@ -58,11 +58,11 @@ public static VimScene LoadVim(Stream stream, IVimSceneProgress progress = null, => 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 IList Meshes { get; private set; } + public IList Nodes { get; private set; } + public IList VimNodes { get; private set; } + public IList VimShapes { get; private set; } + public IList Materials { get; private set; } public SerializableDocument _SerializableDocument { get; } public Document Document { get; private set; } @@ -77,7 +77,7 @@ public Material GetMaterial(int materialIndex) public Vector4 GetMaterialColor(int materialIndex) => _SerializableDocument.Geometry.MaterialColors[materialIndex]; - public static IMesh ToIMesh(G3dMesh g3d) + private static IMesh ToIMesh(G3dMesh g3d) => Primitives.TriMesh( g3d.Vertices.ToPositionAttribute(), g3d.Indices.ToIndexAttribute(), @@ -196,10 +196,10 @@ private void CreateMeshes(bool inParallel) var srcGeo = _SerializableDocument.Geometry; var tmp = srcGeo?.Meshes.Select(ToIMesh); Meshes = (tmp == null) - ? LinqArray.LinqArray.Empty() + ? Array.Empty() : inParallel - ? tmp.EvaluateInParallel() - : tmp.Evaluate(); + ? tmp.AsParallel().ToArray() + : tmp.ToArray(); } private void CreateShapes(bool inParallel) @@ -210,7 +210,7 @@ private void CreateShapes(bool inParallel) } var r = _SerializableDocument.Geometry.Shapes.Select((s, i) => new VimShape(this, i)); - VimShapes = inParallel ? r.EvaluateInParallel() : r.Evaluate(); + VimShapes = inParallel ? r.AsParallel().ToArray() : r.ToArray(); } private void CreateScene(bool inParallel) @@ -221,7 +221,7 @@ private void CreateScene(bool inParallel) } VimNodes = CreateVimSceneNodes(this, _SerializableDocument.Geometry, inParallel); - Nodes = VimNodes.Select(n => n as ISceneNode); + Nodes = VimNodes.Select(n => n as ISceneNode).ToArray(); } private void CreateMaterials(bool inParallel) @@ -232,17 +232,17 @@ private void CreateMaterials(bool inParallel) } var query = _SerializableDocument.Geometry.Materials.Select(m => new VimMaterial(m) as IMaterial); - Materials = inParallel ? query.EvaluateInParallel() : query.Evaluate(); + Materials = inParallel ? query.AsParallel().ToArray() : query.ToArray(); } - public static IArray CreateVimSceneNodes(VimScene scene, G3D g3d, bool inParallel) + public static IList CreateVimSceneNodes(VimScene scene, G3D 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 inParallel ? r.AsParallel().ToArray() : r.ToArray(); } public void Save(string filePath) @@ -253,9 +253,9 @@ public void Save(string filePath) public void TransformSceneInPlace(Func meshTransform = null, Func nodeTransform = null) { if (meshTransform != null) - Meshes = Meshes.Select(meshTransform).EvaluateInParallel(); + Meshes = Meshes.Select(meshTransform).AsParallel().ToArray(); if (nodeTransform != null) - VimNodes = VimNodes.Select(nodeTransform).EvaluateInParallel(); + VimNodes = VimNodes.Select(nodeTransform).AsParallel().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 d98c49bb..635d27de 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 IList MaterialColors(this VimScene scene) => scene.DocumentModel.MaterialList.Select(GetDiffuseColor).ToArray(); public static Vector4 DefaultColor = new Vector4(0.5f, 0.5f, 0.5f, 1); diff --git a/src/cs/vim/Vim.Format/SceneBuilder/VimShape.cs b/src/cs/vim/Vim.Format/SceneBuilder/VimShape.cs index 112a6a13..dfbd4c71 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/VimShape.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/VimShape.cs @@ -1,6 +1,6 @@ -using Vim.Format.ObjectModel; +using System.Collections.Generic; +using Vim.Format.ObjectModel; using Vim.G3d; -using Vim.LinqArray; using Vim.Math3d; namespace Vim @@ -11,7 +11,7 @@ public class VimShape : ElementInfo public readonly int ShapeIndex; public G3dShape G3dShape => Scene.Document.Geometry.Shapes[ShapeIndex]; - public IArray Vertices => G3dShape.Vertices; + public IList Vertices => G3dShape.Vertices; public Vector4 Color => G3dShape.Color; public float Width => G3dShape.Width; From 5d3ec436feee413f1b19f3310a6e3db1b85dcfed Mon Sep 17 00:00:00 2001 From: Vadym Vorotilin Date: Thu, 12 Dec 2024 17:29:14 +0200 Subject: [PATCH 08/22] ObjectModel refactoring: IArray->IList --- src/cs/samples/Vim.JsonDigest/RoomInfo.cs | 3 +- .../ObjectModelGenerator.cs | 11 +- src/cs/vim/Vim.Format.Tests/MergeTests.cs | 9 +- .../vim/Vim.Format/ObjectModel/ElementInfo.cs | 12 +- .../ObjectModel/ObjectModelGenerated.cs | 503 +++++++++--------- .../vim/Vim.Format/ObjectModel/Validation.cs | 43 +- 6 files changed, 288 insertions(+), 293 deletions(-) diff --git a/src/cs/samples/Vim.JsonDigest/RoomInfo.cs b/src/cs/samples/Vim.JsonDigest/RoomInfo.cs index 005f53d0..6a9d6ddc 100644 --- a/src/cs/samples/Vim.JsonDigest/RoomInfo.cs +++ b/src/cs/samples/Vim.JsonDigest/RoomInfo.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Vim.LinqArray; namespace Vim.JsonDigest @@ -63,6 +64,6 @@ public static IEnumerable GetRoomInfoCollection(VimScene vimScene) Area = r.Area, Volume = r.Volume, Perimeter = r.Perimeter - }).ToEnumerable(); + }); } } diff --git a/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs b/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs index c176413c..3c0d8fff 100644 --- a/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs +++ b/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs @@ -92,9 +92,9 @@ private static CodeBuilder WriteDocumentEntityData(Type t, CodeBuilder cb, Entit ? $"({string.Join(" ?? ", dataColumnGetters)})" : dataColumnGetters[0]; - cb.AppendLine($"public IArray<{fieldTypeName}> {t.Name}{fieldName} {{ get; }}"); + cb.AppendLine($"public IList<{fieldTypeName}> {t.Name}{fieldName} {{ get; }}"); constructor.ArraysInitializers - .Add($"{t.Name}{fieldName} = {dataColumnGetterString} ?? Array.Empty<{fieldTypeName}>().ToIArray();"); + .Add($"{t.Name}{fieldName} = {dataColumnGetterString} ?? Array.Empty<{fieldTypeName}>();"); // Safe accessor. var defaultValue = baseStrategy == ValueSerializationStrategy.SerializeAsStringColumn ? "\"\"" : "default"; @@ -106,9 +106,9 @@ private static CodeBuilder WriteDocumentEntityData(Type t, CodeBuilder cb, Entit { var (indexColumnName, localFieldName) = fieldInfo.GetIndexColumnInfo(); - cb.AppendLine($"public IArray {t.Name}{localFieldName}Index {{ get; }}"); + cb.AppendLine($"public IList {t.Name}{localFieldName}Index {{ get; }}"); constructor.RelationalColumns - .Add($"{t.Name}{localFieldName}Index = {t.Name}EntityTable?.GetIndexColumnValues(\"{indexColumnName}\") ?? Array.Empty().ToIArray();"); + .Add($"{t.Name}{localFieldName}Index = {t.Name}EntityTable?.GetIndexColumnValues(\"{indexColumnName}\") ?? Array.Empty();"); cb.AppendLine($"public int Get{t.Name}{localFieldName}Index(int index) => {t.Name}{localFieldName}Index?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None;"); } @@ -117,7 +117,7 @@ private static CodeBuilder WriteDocumentEntityData(Type t, CodeBuilder cb, Entit cb.AppendLine($"public int Num{t.Name} => {t.Name}EntityTable?.NumRows ?? 0;"); // Entity lists - cb.AppendLine($"public IArray<{t.Name}> {t.Name}List {{ get; }}"); + cb.AppendLine($"public IList<{t.Name}> {t.Name}List {{ get; }}"); // Element getter function cb.AppendLine($"public {t.Name} Get{t.Name}(int n)"); @@ -506,7 +506,6 @@ public static void WriteDocument(string file) cb.AppendLine("using System.Collections.Generic;"); cb.AppendLine("using System.Linq;"); cb.AppendLine("using Vim.Math3d;"); - cb.AppendLine("using Vim.LinqArray;"); cb.AppendLine("using Vim.Format.ObjectModel;"); cb.AppendLine("using Vim.Util;"); diff --git a/src/cs/vim/Vim.Format.Tests/MergeTests.cs b/src/cs/vim/Vim.Format.Tests/MergeTests.cs index 417aa3a2..252cd0d3 100644 --- a/src/cs/vim/Vim.Format.Tests/MergeTests.cs +++ b/src/cs/vim/Vim.Format.Tests/MergeTests.cs @@ -6,7 +6,6 @@ using Vim.Format.Merge; using Vim.Format.ObjectModel; using Vim.Format.SceneBuilder; -using Vim.LinqArray; using Vim.Math3d; using Vim.Util; using Vim.Util.Tests; @@ -105,15 +104,15 @@ public static void TestMergeDifferentFilesAsGrid() mergedVim.Validate(); // The categories in the merged VIM must be a distinct count of the categories in vim1 and vim2. - var categoriesVim1 = vim1.DocumentModel.CategoryList.ToEnumerable(); - var categoriesVim2 = vim2.DocumentModel.CategoryList.ToEnumerable(); + var categoriesVim1 = vim1.DocumentModel.CategoryList; + var categoriesVim2 = vim2.DocumentModel.CategoryList; var distinctCategoryCount = categoriesVim1.Concat(categoriesVim2).Distinct(new CategoryEqualityComparer()).Count(); var mergedCategoryCount = mergedVim.DocumentModel.NumCategory; Assert.AreEqual(distinctCategoryCount, mergedCategoryCount); // The display units in the merged VIM must be a distinct count of the display units in vim1 and vim2. - var displayUnitsVim1 = vim1.DocumentModel.DisplayUnitList.ToEnumerable(); - var displayUnitsVim2 = vim2.DocumentModel.DisplayUnitList.ToEnumerable(); + var displayUnitsVim1 = vim1.DocumentModel.DisplayUnitList; + var displayUnitsVim2 = vim2.DocumentModel.DisplayUnitList; var distinctDisplayUnitCount = displayUnitsVim1.Concat(displayUnitsVim2).Distinct(new DisplayUnitEqualityComparer()).Count(); var mergedDisplayUnitCount = mergedVim.DocumentModel.NumDisplayUnit; Assert.AreEqual(distinctDisplayUnitCount, mergedDisplayUnitCount); diff --git a/src/cs/vim/Vim.Format/ObjectModel/ElementInfo.cs b/src/cs/vim/Vim.Format/ObjectModel/ElementInfo.cs index d44c75de..0bcf84da 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/ElementInfo.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/ElementInfo.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; -using Vim.LinqArray; +using Vim.G3d; namespace Vim.Format.ObjectModel { @@ -30,8 +29,8 @@ public int FamilyInstanceIndex } } - private IArray _parameterIndices; - public IArray ParameterIndices + private IList _parameterIndices; + public IList ParameterIndices { get { @@ -39,8 +38,7 @@ public IArray ParameterIndices return _parameterIndices; _parameterIndices = (DocumentModel.ElementIndexMaps.ParameterIndicesFromElementIndex - .TryGetValue(ElementIndex, out var parameterIndices) ? parameterIndices : new List()) - .ToIArray(); + .TryGetValue(ElementIndex, out var parameterIndices) ? parameterIndices.ToArray() : Array.Empty()); return _parameterIndices; } @@ -109,7 +107,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 => DocumentModel.ParameterList.SelectByIndex(ParameterIndices); [Flags] public enum ParameterScope diff --git a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs index 50f7c8af..c7fabab8 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using Vim.Math3d; -using Vim.LinqArray; using Vim.Format.ObjectModel; using Vim.Util; @@ -1894,10 +1893,10 @@ public partial class DocumentModel public EntityTable AssetEntityTable { get; } - public IArray AssetBufferName { get; } + public IList 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 IList AssetList { get; } public Asset GetAsset(int n) { if (n < 0) return null; @@ -1913,14 +1912,14 @@ public Asset GetAsset(int n) public EntityTable DisplayUnitEntityTable { get; } - public IArray DisplayUnitSpec { get; } + public IList DisplayUnitSpec { get; } public String GetDisplayUnitSpec(int index, String defaultValue = "") => DisplayUnitSpec?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray DisplayUnitType { get; } + public IList DisplayUnitType { get; } public String GetDisplayUnitType(int index, String defaultValue = "") => DisplayUnitType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray DisplayUnitLabel { get; } + public IList 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 IList DisplayUnitList { get; } public DisplayUnit GetDisplayUnit(int n) { if (n < 0) return null; @@ -1938,28 +1937,28 @@ public DisplayUnit GetDisplayUnit(int n) public EntityTable ParameterDescriptorEntityTable { get; } - public IArray ParameterDescriptorName { get; } + public IList ParameterDescriptorName { get; } public String GetParameterDescriptorName(int index, String defaultValue = "") => ParameterDescriptorName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorGroup { get; } + public IList ParameterDescriptorGroup { get; } public String GetParameterDescriptorGroup(int index, String defaultValue = "") => ParameterDescriptorGroup?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorParameterType { get; } + public IList ParameterDescriptorParameterType { get; } public String GetParameterDescriptorParameterType(int index, String defaultValue = "") => ParameterDescriptorParameterType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorIsInstance { get; } + public IList ParameterDescriptorIsInstance { get; } public Boolean GetParameterDescriptorIsInstance(int index, Boolean defaultValue = default) => ParameterDescriptorIsInstance?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorIsShared { get; } + public IList ParameterDescriptorIsShared { get; } public Boolean GetParameterDescriptorIsShared(int index, Boolean defaultValue = default) => ParameterDescriptorIsShared?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorIsReadOnly { get; } + public IList ParameterDescriptorIsReadOnly { get; } public Boolean GetParameterDescriptorIsReadOnly(int index, Boolean defaultValue = default) => ParameterDescriptorIsReadOnly?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorFlags { get; } + public IList ParameterDescriptorFlags { get; } public Int32 GetParameterDescriptorFlags(int index, Int32 defaultValue = default) => ParameterDescriptorFlags?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorGuid { get; } + public IList ParameterDescriptorGuid { get; } public String GetParameterDescriptorGuid(int index, String defaultValue = "") => ParameterDescriptorGuid?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorStorageType { get; } + public IList ParameterDescriptorStorageType { get; } public Int32 GetParameterDescriptorStorageType(int index, Int32 defaultValue = default) => ParameterDescriptorStorageType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterDescriptorDisplayUnitIndex { get; } + public IList 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 IList ParameterDescriptorList { get; } public ParameterDescriptor GetParameterDescriptor(int n) { if (n < 0) return null; @@ -1984,14 +1983,14 @@ public ParameterDescriptor GetParameterDescriptor(int n) public EntityTable ParameterEntityTable { get; } - public IArray ParameterValue { get; } + public IList ParameterValue { get; } public String GetParameterValue(int index, String defaultValue = "") => ParameterValue?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ParameterParameterDescriptorIndex { get; } + public IList ParameterParameterDescriptorIndex { get; } public int GetParameterParameterDescriptorIndex(int index) => ParameterParameterDescriptorIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ParameterElementIndex { get; } + public IList 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 IList ParameterList { get; } public Parameter GetParameter(int n) { if (n < 0) return null; @@ -2009,48 +2008,48 @@ public Parameter GetParameter(int n) public EntityTable ElementEntityTable { get; } - public IArray ElementId { get; } + public IList ElementId { get; } public Int64 GetElementId(int index, Int64 defaultValue = default) => ElementId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementType { get; } + public IList ElementType { get; } public String GetElementType(int index, String defaultValue = "") => ElementType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementName { get; } + public IList ElementName { get; } public String GetElementName(int index, String defaultValue = "") => ElementName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementUniqueId { get; } + public IList ElementUniqueId { get; } public String GetElementUniqueId(int index, String defaultValue = "") => ElementUniqueId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementLocation_X { get; } + public IList ElementLocation_X { get; } public Single GetElementLocation_X(int index, Single defaultValue = default) => ElementLocation_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementLocation_Y { get; } + public IList ElementLocation_Y { get; } public Single GetElementLocation_Y(int index, Single defaultValue = default) => ElementLocation_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementLocation_Z { get; } + public IList ElementLocation_Z { get; } public Single GetElementLocation_Z(int index, Single defaultValue = default) => ElementLocation_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementFamilyName { get; } + public IList ElementFamilyName { get; } public String GetElementFamilyName(int index, String defaultValue = "") => ElementFamilyName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementIsPinned { get; } + public IList ElementIsPinned { get; } public Boolean GetElementIsPinned(int index, Boolean defaultValue = default) => ElementIsPinned?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ElementLevelIndex { get; } + public IList ElementLevelIndex { get; } public int GetElementLevelIndex(int index) => ElementLevelIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementPhaseCreatedIndex { get; } + public IList ElementPhaseCreatedIndex { get; } public int GetElementPhaseCreatedIndex(int index) => ElementPhaseCreatedIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementPhaseDemolishedIndex { get; } + public IList ElementPhaseDemolishedIndex { get; } public int GetElementPhaseDemolishedIndex(int index) => ElementPhaseDemolishedIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementCategoryIndex { get; } + public IList ElementCategoryIndex { get; } public int GetElementCategoryIndex(int index) => ElementCategoryIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementWorksetIndex { get; } + public IList ElementWorksetIndex { get; } public int GetElementWorksetIndex(int index) => ElementWorksetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementDesignOptionIndex { get; } + public IList ElementDesignOptionIndex { get; } public int GetElementDesignOptionIndex(int index) => ElementDesignOptionIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementOwnerViewIndex { get; } + public IList ElementOwnerViewIndex { get; } public int GetElementOwnerViewIndex(int index) => ElementOwnerViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementGroupIndex { get; } + public IList ElementGroupIndex { get; } public int GetElementGroupIndex(int index) => ElementGroupIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementAssemblyInstanceIndex { get; } + public IList ElementAssemblyInstanceIndex { get; } public int GetElementAssemblyInstanceIndex(int index) => ElementAssemblyInstanceIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementBimDocumentIndex { get; } + public IList ElementBimDocumentIndex { get; } public int GetElementBimDocumentIndex(int index) => ElementBimDocumentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementRoomIndex { get; } + public IList 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 IList ElementList { get; } public Element GetElement(int n) { if (n < 0) return null; @@ -2085,24 +2084,24 @@ public Element GetElement(int n) public EntityTable WorksetEntityTable { get; } - public IArray WorksetId { get; } + public IList WorksetId { get; } public Int32 GetWorksetId(int index, Int32 defaultValue = default) => WorksetId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WorksetName { get; } + public IList WorksetName { get; } public String GetWorksetName(int index, String defaultValue = "") => WorksetName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WorksetKind { get; } + public IList WorksetKind { get; } public String GetWorksetKind(int index, String defaultValue = "") => WorksetKind?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WorksetIsOpen { get; } + public IList WorksetIsOpen { get; } public Boolean GetWorksetIsOpen(int index, Boolean defaultValue = default) => WorksetIsOpen?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WorksetIsEditable { get; } + public IList WorksetIsEditable { get; } public Boolean GetWorksetIsEditable(int index, Boolean defaultValue = default) => WorksetIsEditable?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WorksetOwner { get; } + public IList WorksetOwner { get; } public String GetWorksetOwner(int index, String defaultValue = "") => WorksetOwner?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WorksetUniqueId { get; } + public IList WorksetUniqueId { get; } public String GetWorksetUniqueId(int index, String defaultValue = "") => WorksetUniqueId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray WorksetBimDocumentIndex { get; } + public IList 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 IList WorksetList { get; } public Workset GetWorkset(int n) { if (n < 0) return null; @@ -2125,18 +2124,18 @@ public Workset GetWorkset(int n) public EntityTable AssemblyInstanceEntityTable { get; } - public IArray AssemblyInstanceAssemblyTypeName { get; } + public IList AssemblyInstanceAssemblyTypeName { get; } public String GetAssemblyInstanceAssemblyTypeName(int index, String defaultValue = "") => AssemblyInstanceAssemblyTypeName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AssemblyInstancePosition_X { get; } + public IList AssemblyInstancePosition_X { get; } public Single GetAssemblyInstancePosition_X(int index, Single defaultValue = default) => AssemblyInstancePosition_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AssemblyInstancePosition_Y { get; } + public IList AssemblyInstancePosition_Y { get; } public Single GetAssemblyInstancePosition_Y(int index, Single defaultValue = default) => AssemblyInstancePosition_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AssemblyInstancePosition_Z { get; } + public IList AssemblyInstancePosition_Z { get; } public Single GetAssemblyInstancePosition_Z(int index, Single defaultValue = default) => AssemblyInstancePosition_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray AssemblyInstanceElementIndex { get; } + public IList 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 IList AssemblyInstanceList { get; } public AssemblyInstance GetAssemblyInstance(int n) { if (n < 0) return null; @@ -2156,18 +2155,18 @@ public AssemblyInstance GetAssemblyInstance(int n) public EntityTable GroupEntityTable { get; } - public IArray GroupGroupType { get; } + public IList GroupGroupType { get; } public String GetGroupGroupType(int index, String defaultValue = "") => GroupGroupType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GroupPosition_X { get; } + public IList GroupPosition_X { get; } public Single GetGroupPosition_X(int index, Single defaultValue = default) => GroupPosition_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GroupPosition_Y { get; } + public IList GroupPosition_Y { get; } public Single GetGroupPosition_Y(int index, Single defaultValue = default) => GroupPosition_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GroupPosition_Z { get; } + public IList GroupPosition_Z { get; } public Single GetGroupPosition_Z(int index, Single defaultValue = default) => GroupPosition_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray GroupElementIndex { get; } + public IList 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 IList GroupList { get; } public Group GetGroup(int n) { if (n < 0) return null; @@ -2187,12 +2186,12 @@ public Group GetGroup(int n) public EntityTable DesignOptionEntityTable { get; } - public IArray DesignOptionIsPrimary { get; } + public IList DesignOptionIsPrimary { get; } public Boolean GetDesignOptionIsPrimary(int index, Boolean defaultValue = default) => DesignOptionIsPrimary?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray DesignOptionElementIndex { get; } + public IList 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 IList DesignOptionList { get; } public DesignOption GetDesignOption(int n) { if (n < 0) return null; @@ -2209,16 +2208,16 @@ public DesignOption GetDesignOption(int n) public EntityTable LevelEntityTable { get; } - public IArray LevelElevation { get; } + public IList LevelElevation { get; } public Double GetLevelElevation(int index, Double defaultValue = default) => LevelElevation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray LevelFamilyTypeIndex { get; } + public IList LevelFamilyTypeIndex { get; } public int GetLevelFamilyTypeIndex(int index) => LevelFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray LevelBuildingIndex { get; } + public IList LevelBuildingIndex { get; } public int GetLevelBuildingIndex(int index) => LevelBuildingIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray LevelElementIndex { get; } + public IList 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 IList LevelList { get; } public Level GetLevel(int n) { if (n < 0) return null; @@ -2237,10 +2236,10 @@ public Level GetLevel(int n) public EntityTable PhaseEntityTable { get; } - public IArray PhaseElementIndex { get; } + public IList 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 IList PhaseList { get; } public Phase GetPhase(int n) { if (n < 0) return null; @@ -2256,26 +2255,26 @@ public Phase GetPhase(int n) public EntityTable RoomEntityTable { get; } - public IArray RoomBaseOffset { get; } + public IList RoomBaseOffset { get; } public Double GetRoomBaseOffset(int index, Double defaultValue = default) => RoomBaseOffset?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray RoomLimitOffset { get; } + public IList RoomLimitOffset { get; } public Double GetRoomLimitOffset(int index, Double defaultValue = default) => RoomLimitOffset?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray RoomUnboundedHeight { get; } + public IList RoomUnboundedHeight { get; } public Double GetRoomUnboundedHeight(int index, Double defaultValue = default) => RoomUnboundedHeight?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray RoomVolume { get; } + public IList RoomVolume { get; } public Double GetRoomVolume(int index, Double defaultValue = default) => RoomVolume?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray RoomPerimeter { get; } + public IList RoomPerimeter { get; } public Double GetRoomPerimeter(int index, Double defaultValue = default) => RoomPerimeter?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray RoomArea { get; } + public IList RoomArea { get; } public Double GetRoomArea(int index, Double defaultValue = default) => RoomArea?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray RoomNumber { get; } + public IList RoomNumber { get; } public String GetRoomNumber(int index, String defaultValue = "") => RoomNumber?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray RoomUpperLimitIndex { get; } + public IList RoomUpperLimitIndex { get; } public int GetRoomUpperLimitIndex(int index) => RoomUpperLimitIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray RoomElementIndex { get; } + public IList 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 IList RoomList { get; } public Room GetRoom(int n) { if (n < 0) return null; @@ -2299,72 +2298,72 @@ public Room GetRoom(int n) public EntityTable BimDocumentEntityTable { get; } - public IArray BimDocumentTitle { get; } + public IList BimDocumentTitle { get; } public String GetBimDocumentTitle(int index, String defaultValue = "") => BimDocumentTitle?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentIsMetric { get; } + public IList BimDocumentIsMetric { get; } public Boolean GetBimDocumentIsMetric(int index, Boolean defaultValue = default) => BimDocumentIsMetric?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentGuid { get; } + public IList BimDocumentGuid { get; } public String GetBimDocumentGuid(int index, String defaultValue = "") => BimDocumentGuid?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentNumSaves { get; } + public IList BimDocumentNumSaves { get; } public Int32 GetBimDocumentNumSaves(int index, Int32 defaultValue = default) => BimDocumentNumSaves?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentIsLinked { get; } + public IList BimDocumentIsLinked { get; } public Boolean GetBimDocumentIsLinked(int index, Boolean defaultValue = default) => BimDocumentIsLinked?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentIsDetached { get; } + public IList BimDocumentIsDetached { get; } public Boolean GetBimDocumentIsDetached(int index, Boolean defaultValue = default) => BimDocumentIsDetached?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentIsWorkshared { get; } + public IList BimDocumentIsWorkshared { get; } public Boolean GetBimDocumentIsWorkshared(int index, Boolean defaultValue = default) => BimDocumentIsWorkshared?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentPathName { get; } + public IList BimDocumentPathName { get; } public String GetBimDocumentPathName(int index, String defaultValue = "") => BimDocumentPathName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentLatitude { get; } + public IList BimDocumentLatitude { get; } public Double GetBimDocumentLatitude(int index, Double defaultValue = default) => BimDocumentLatitude?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentLongitude { get; } + public IList BimDocumentLongitude { get; } public Double GetBimDocumentLongitude(int index, Double defaultValue = default) => BimDocumentLongitude?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentTimeZone { get; } + public IList BimDocumentTimeZone { get; } public Double GetBimDocumentTimeZone(int index, Double defaultValue = default) => BimDocumentTimeZone?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentPlaceName { get; } + public IList BimDocumentPlaceName { get; } public String GetBimDocumentPlaceName(int index, String defaultValue = "") => BimDocumentPlaceName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentWeatherStationName { get; } + public IList BimDocumentWeatherStationName { get; } public String GetBimDocumentWeatherStationName(int index, String defaultValue = "") => BimDocumentWeatherStationName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentElevation { get; } + public IList BimDocumentElevation { get; } public Double GetBimDocumentElevation(int index, Double defaultValue = default) => BimDocumentElevation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentProjectLocation { get; } + public IList BimDocumentProjectLocation { get; } public String GetBimDocumentProjectLocation(int index, String defaultValue = "") => BimDocumentProjectLocation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentIssueDate { get; } + public IList BimDocumentIssueDate { get; } public String GetBimDocumentIssueDate(int index, String defaultValue = "") => BimDocumentIssueDate?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentStatus { get; } + public IList BimDocumentStatus { get; } public String GetBimDocumentStatus(int index, String defaultValue = "") => BimDocumentStatus?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentClientName { get; } + public IList BimDocumentClientName { get; } public String GetBimDocumentClientName(int index, String defaultValue = "") => BimDocumentClientName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentAddress { get; } + public IList BimDocumentAddress { get; } public String GetBimDocumentAddress(int index, String defaultValue = "") => BimDocumentAddress?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentName { get; } + public IList BimDocumentName { get; } public String GetBimDocumentName(int index, String defaultValue = "") => BimDocumentName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentNumber { get; } + public IList BimDocumentNumber { get; } public String GetBimDocumentNumber(int index, String defaultValue = "") => BimDocumentNumber?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentAuthor { get; } + public IList BimDocumentAuthor { get; } public String GetBimDocumentAuthor(int index, String defaultValue = "") => BimDocumentAuthor?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentBuildingName { get; } + public IList BimDocumentBuildingName { get; } public String GetBimDocumentBuildingName(int index, String defaultValue = "") => BimDocumentBuildingName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentOrganizationName { get; } + public IList BimDocumentOrganizationName { get; } public String GetBimDocumentOrganizationName(int index, String defaultValue = "") => BimDocumentOrganizationName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentOrganizationDescription { get; } + public IList BimDocumentOrganizationDescription { get; } public String GetBimDocumentOrganizationDescription(int index, String defaultValue = "") => BimDocumentOrganizationDescription?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentProduct { get; } + public IList BimDocumentProduct { get; } public String GetBimDocumentProduct(int index, String defaultValue = "") => BimDocumentProduct?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentVersion { get; } + public IList BimDocumentVersion { get; } public String GetBimDocumentVersion(int index, String defaultValue = "") => BimDocumentVersion?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentUser { get; } + public IList BimDocumentUser { get; } public String GetBimDocumentUser(int index, String defaultValue = "") => BimDocumentUser?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray BimDocumentActiveViewIndex { get; } + public IList BimDocumentActiveViewIndex { get; } public int GetBimDocumentActiveViewIndex(int index) => BimDocumentActiveViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray BimDocumentOwnerFamilyIndex { get; } + public IList BimDocumentOwnerFamilyIndex { get; } public int GetBimDocumentOwnerFamilyIndex(int index) => BimDocumentOwnerFamilyIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray BimDocumentParentIndex { get; } + public IList BimDocumentParentIndex { get; } public int GetBimDocumentParentIndex(int index) => BimDocumentParentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray BimDocumentElementIndex { get; } + public IList 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 IList BimDocumentList { get; } public BimDocument GetBimDocument(int n) { if (n < 0) return null; @@ -2411,12 +2410,12 @@ public BimDocument GetBimDocument(int n) public EntityTable DisplayUnitInBimDocumentEntityTable { get; } - public IArray DisplayUnitInBimDocumentDisplayUnitIndex { get; } + public IList DisplayUnitInBimDocumentDisplayUnitIndex { get; } public int GetDisplayUnitInBimDocumentDisplayUnitIndex(int index) => DisplayUnitInBimDocumentDisplayUnitIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray DisplayUnitInBimDocumentBimDocumentIndex { get; } + public IList 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 IList DisplayUnitInBimDocumentList { get; } public DisplayUnitInBimDocument GetDisplayUnitInBimDocument(int n) { if (n < 0) return null; @@ -2433,14 +2432,14 @@ public DisplayUnitInBimDocument GetDisplayUnitInBimDocument(int n) public EntityTable PhaseOrderInBimDocumentEntityTable { get; } - public IArray PhaseOrderInBimDocumentOrderIndex { get; } + public IList PhaseOrderInBimDocumentOrderIndex { get; } public Int32 GetPhaseOrderInBimDocumentOrderIndex(int index, Int32 defaultValue = default) => PhaseOrderInBimDocumentOrderIndex?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray PhaseOrderInBimDocumentPhaseIndex { get; } + public IList PhaseOrderInBimDocumentPhaseIndex { get; } public int GetPhaseOrderInBimDocumentPhaseIndex(int index) => PhaseOrderInBimDocumentPhaseIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray PhaseOrderInBimDocumentBimDocumentIndex { get; } + public IList 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 IList PhaseOrderInBimDocumentList { get; } public PhaseOrderInBimDocument GetPhaseOrderInBimDocument(int n) { if (n < 0) return null; @@ -2458,26 +2457,26 @@ public PhaseOrderInBimDocument GetPhaseOrderInBimDocument(int n) public EntityTable CategoryEntityTable { get; } - public IArray CategoryName { get; } + public IList CategoryName { get; } public String GetCategoryName(int index, String defaultValue = "") => CategoryName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CategoryId { get; } + public IList CategoryId { get; } public Int64 GetCategoryId(int index, Int64 defaultValue = default) => CategoryId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CategoryCategoryType { get; } + public IList CategoryCategoryType { get; } public String GetCategoryCategoryType(int index, String defaultValue = "") => CategoryCategoryType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CategoryLineColor_X { get; } + public IList CategoryLineColor_X { get; } public Double GetCategoryLineColor_X(int index, Double defaultValue = default) => CategoryLineColor_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CategoryLineColor_Y { get; } + public IList CategoryLineColor_Y { get; } public Double GetCategoryLineColor_Y(int index, Double defaultValue = default) => CategoryLineColor_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CategoryLineColor_Z { get; } + public IList CategoryLineColor_Z { get; } public Double GetCategoryLineColor_Z(int index, Double defaultValue = default) => CategoryLineColor_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CategoryBuiltInCategory { get; } + public IList CategoryBuiltInCategory { get; } public String GetCategoryBuiltInCategory(int index, String defaultValue = "") => CategoryBuiltInCategory?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CategoryParentIndex { get; } + public IList CategoryParentIndex { get; } public int GetCategoryParentIndex(int index) => CategoryParentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray CategoryMaterialIndex { get; } + public IList 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 IList CategoryList { get; } public Category GetCategory(int n) { if (n < 0) return null; @@ -2501,20 +2500,20 @@ public Category GetCategory(int n) public EntityTable FamilyEntityTable { get; } - public IArray FamilyStructuralMaterialType { get; } + public IList FamilyStructuralMaterialType { get; } public String GetFamilyStructuralMaterialType(int index, String defaultValue = "") => FamilyStructuralMaterialType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyStructuralSectionShape { get; } + public IList FamilyStructuralSectionShape { get; } public String GetFamilyStructuralSectionShape(int index, String defaultValue = "") => FamilyStructuralSectionShape?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyIsSystemFamily { get; } + public IList FamilyIsSystemFamily { get; } public Boolean GetFamilyIsSystemFamily(int index, Boolean defaultValue = default) => FamilyIsSystemFamily?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyIsInPlace { get; } + public IList FamilyIsInPlace { get; } public Boolean GetFamilyIsInPlace(int index, Boolean defaultValue = default) => FamilyIsInPlace?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyFamilyCategoryIndex { get; } + public IList FamilyFamilyCategoryIndex { get; } public int GetFamilyFamilyCategoryIndex(int index) => FamilyFamilyCategoryIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyElementIndex { get; } + public IList 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 IList FamilyList { get; } public Family GetFamily(int n) { if (n < 0) return null; @@ -2535,16 +2534,16 @@ public Family GetFamily(int n) public EntityTable FamilyTypeEntityTable { get; } - public IArray FamilyTypeIsSystemFamilyType { get; } + public IList FamilyTypeIsSystemFamilyType { get; } public Boolean GetFamilyTypeIsSystemFamilyType(int index, Boolean defaultValue = default) => FamilyTypeIsSystemFamilyType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyTypeFamilyIndex { get; } + public IList FamilyTypeFamilyIndex { get; } public int GetFamilyTypeFamilyIndex(int index) => FamilyTypeFamilyIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyTypeCompoundStructureIndex { get; } + public IList FamilyTypeCompoundStructureIndex { get; } public int GetFamilyTypeCompoundStructureIndex(int index) => FamilyTypeCompoundStructureIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyTypeElementIndex { get; } + public IList 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 IList FamilyTypeList { get; } public FamilyType GetFamilyType(int n) { if (n < 0) return null; @@ -2563,66 +2562,66 @@ public FamilyType GetFamilyType(int n) public EntityTable FamilyInstanceEntityTable { get; } - public IArray FamilyInstanceFacingFlipped { get; } + public IList FamilyInstanceFacingFlipped { get; } public Boolean GetFamilyInstanceFacingFlipped(int index, Boolean defaultValue = default) => FamilyInstanceFacingFlipped?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceFacingOrientation_X { get; } + public IList FamilyInstanceFacingOrientation_X { get; } public Single GetFamilyInstanceFacingOrientation_X(int index, Single defaultValue = default) => FamilyInstanceFacingOrientation_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceFacingOrientation_Y { get; } + public IList FamilyInstanceFacingOrientation_Y { get; } public Single GetFamilyInstanceFacingOrientation_Y(int index, Single defaultValue = default) => FamilyInstanceFacingOrientation_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceFacingOrientation_Z { get; } + public IList FamilyInstanceFacingOrientation_Z { get; } public Single GetFamilyInstanceFacingOrientation_Z(int index, Single defaultValue = default) => FamilyInstanceFacingOrientation_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceHandFlipped { get; } + public IList FamilyInstanceHandFlipped { get; } public Boolean GetFamilyInstanceHandFlipped(int index, Boolean defaultValue = default) => FamilyInstanceHandFlipped?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceMirrored { get; } + public IList FamilyInstanceMirrored { get; } public Boolean GetFamilyInstanceMirrored(int index, Boolean defaultValue = default) => FamilyInstanceMirrored?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceHasModifiedGeometry { get; } + public IList FamilyInstanceHasModifiedGeometry { get; } public Boolean GetFamilyInstanceHasModifiedGeometry(int index, Boolean defaultValue = default) => FamilyInstanceHasModifiedGeometry?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceScale { get; } + public IList FamilyInstanceScale { get; } public Single GetFamilyInstanceScale(int index, Single defaultValue = default) => FamilyInstanceScale?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisX_X { get; } + public IList FamilyInstanceBasisX_X { get; } public Single GetFamilyInstanceBasisX_X(int index, Single defaultValue = default) => FamilyInstanceBasisX_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisX_Y { get; } + public IList FamilyInstanceBasisX_Y { get; } public Single GetFamilyInstanceBasisX_Y(int index, Single defaultValue = default) => FamilyInstanceBasisX_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisX_Z { get; } + public IList FamilyInstanceBasisX_Z { get; } public Single GetFamilyInstanceBasisX_Z(int index, Single defaultValue = default) => FamilyInstanceBasisX_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisY_X { get; } + public IList FamilyInstanceBasisY_X { get; } public Single GetFamilyInstanceBasisY_X(int index, Single defaultValue = default) => FamilyInstanceBasisY_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisY_Y { get; } + public IList FamilyInstanceBasisY_Y { get; } public Single GetFamilyInstanceBasisY_Y(int index, Single defaultValue = default) => FamilyInstanceBasisY_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisY_Z { get; } + public IList FamilyInstanceBasisY_Z { get; } public Single GetFamilyInstanceBasisY_Z(int index, Single defaultValue = default) => FamilyInstanceBasisY_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisZ_X { get; } + public IList FamilyInstanceBasisZ_X { get; } public Single GetFamilyInstanceBasisZ_X(int index, Single defaultValue = default) => FamilyInstanceBasisZ_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisZ_Y { get; } + public IList FamilyInstanceBasisZ_Y { get; } public Single GetFamilyInstanceBasisZ_Y(int index, Single defaultValue = default) => FamilyInstanceBasisZ_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceBasisZ_Z { get; } + public IList FamilyInstanceBasisZ_Z { get; } public Single GetFamilyInstanceBasisZ_Z(int index, Single defaultValue = default) => FamilyInstanceBasisZ_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceTranslation_X { get; } + public IList FamilyInstanceTranslation_X { get; } public Single GetFamilyInstanceTranslation_X(int index, Single defaultValue = default) => FamilyInstanceTranslation_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceTranslation_Y { get; } + public IList FamilyInstanceTranslation_Y { get; } public Single GetFamilyInstanceTranslation_Y(int index, Single defaultValue = default) => FamilyInstanceTranslation_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceTranslation_Z { get; } + public IList FamilyInstanceTranslation_Z { get; } public Single GetFamilyInstanceTranslation_Z(int index, Single defaultValue = default) => FamilyInstanceTranslation_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceHandOrientation_X { get; } + public IList FamilyInstanceHandOrientation_X { get; } public Single GetFamilyInstanceHandOrientation_X(int index, Single defaultValue = default) => FamilyInstanceHandOrientation_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceHandOrientation_Y { get; } + public IList FamilyInstanceHandOrientation_Y { get; } public Single GetFamilyInstanceHandOrientation_Y(int index, Single defaultValue = default) => FamilyInstanceHandOrientation_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceHandOrientation_Z { get; } + public IList FamilyInstanceHandOrientation_Z { get; } public Single GetFamilyInstanceHandOrientation_Z(int index, Single defaultValue = default) => FamilyInstanceHandOrientation_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray FamilyInstanceFamilyTypeIndex { get; } + public IList FamilyInstanceFamilyTypeIndex { get; } public int GetFamilyInstanceFamilyTypeIndex(int index) => FamilyInstanceFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyInstanceHostIndex { get; } + public IList FamilyInstanceHostIndex { get; } public int GetFamilyInstanceHostIndex(int index) => FamilyInstanceHostIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyInstanceFromRoomIndex { get; } + public IList FamilyInstanceFromRoomIndex { get; } public int GetFamilyInstanceFromRoomIndex(int index) => FamilyInstanceFromRoomIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyInstanceToRoomIndex { get; } + public IList FamilyInstanceToRoomIndex { get; } public int GetFamilyInstanceToRoomIndex(int index) => FamilyInstanceToRoomIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyInstanceSuperComponentIndex { get; } + public IList FamilyInstanceSuperComponentIndex { get; } public int GetFamilyInstanceSuperComponentIndex(int index) => FamilyInstanceSuperComponentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray FamilyInstanceElementIndex { get; } + public IList 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 IList FamilyInstanceList { get; } public FamilyInstance GetFamilyInstance(int n) { if (n < 0) return null; @@ -2666,60 +2665,60 @@ public FamilyInstance GetFamilyInstance(int n) public EntityTable ViewEntityTable { get; } - public IArray ViewTitle { get; } + public IList ViewTitle { get; } public String GetViewTitle(int index, String defaultValue = "") => ViewTitle?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewViewType { get; } + public IList ViewViewType { get; } public String GetViewViewType(int index, String defaultValue = "") => ViewViewType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewUp_X { get; } + public IList ViewUp_X { get; } public Double GetViewUp_X(int index, Double defaultValue = default) => ViewUp_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewUp_Y { get; } + public IList ViewUp_Y { get; } public Double GetViewUp_Y(int index, Double defaultValue = default) => ViewUp_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewUp_Z { get; } + public IList ViewUp_Z { get; } public Double GetViewUp_Z(int index, Double defaultValue = default) => ViewUp_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewRight_X { get; } + public IList ViewRight_X { get; } public Double GetViewRight_X(int index, Double defaultValue = default) => ViewRight_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewRight_Y { get; } + public IList ViewRight_Y { get; } public Double GetViewRight_Y(int index, Double defaultValue = default) => ViewRight_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewRight_Z { get; } + public IList ViewRight_Z { get; } public Double GetViewRight_Z(int index, Double defaultValue = default) => ViewRight_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewOrigin_X { get; } + public IList ViewOrigin_X { get; } public Double GetViewOrigin_X(int index, Double defaultValue = default) => ViewOrigin_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewOrigin_Y { get; } + public IList ViewOrigin_Y { get; } public Double GetViewOrigin_Y(int index, Double defaultValue = default) => ViewOrigin_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewOrigin_Z { get; } + public IList ViewOrigin_Z { get; } public Double GetViewOrigin_Z(int index, Double defaultValue = default) => ViewOrigin_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewViewDirection_X { get; } + public IList ViewViewDirection_X { get; } public Double GetViewViewDirection_X(int index, Double defaultValue = default) => ViewViewDirection_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewViewDirection_Y { get; } + public IList ViewViewDirection_Y { get; } public Double GetViewViewDirection_Y(int index, Double defaultValue = default) => ViewViewDirection_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewViewDirection_Z { get; } + public IList ViewViewDirection_Z { get; } public Double GetViewViewDirection_Z(int index, Double defaultValue = default) => ViewViewDirection_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewViewPosition_X { get; } + public IList ViewViewPosition_X { get; } public Double GetViewViewPosition_X(int index, Double defaultValue = default) => ViewViewPosition_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewViewPosition_Y { get; } + public IList ViewViewPosition_Y { get; } public Double GetViewViewPosition_Y(int index, Double defaultValue = default) => ViewViewPosition_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewViewPosition_Z { get; } + public IList ViewViewPosition_Z { get; } public Double GetViewViewPosition_Z(int index, Double defaultValue = default) => ViewViewPosition_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewScale { get; } + public IList ViewScale { get; } public Double GetViewScale(int index, Double defaultValue = default) => ViewScale?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewOutline_Min_X { get; } + public IList 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 IList 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 IList 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 IList 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 IList ViewDetailLevel { get; } public Int32 GetViewDetailLevel(int index, Int32 defaultValue = default) => ViewDetailLevel?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray ViewCameraIndex { get; } + public IList ViewCameraIndex { get; } public int GetViewCameraIndex(int index) => ViewCameraIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ViewFamilyTypeIndex { get; } + public IList ViewFamilyTypeIndex { get; } public int GetViewFamilyTypeIndex(int index) => ViewFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ViewElementIndex { get; } + public IList 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 IList ViewList { get; } public View GetView(int n) { if (n < 0) return null; @@ -2760,12 +2759,12 @@ public View GetView(int n) public EntityTable ElementInViewEntityTable { get; } - public IArray ElementInViewViewIndex { get; } + public IList ElementInViewViewIndex { get; } public int GetElementInViewViewIndex(int index) => ElementInViewViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ElementInViewElementIndex { get; } + public IList 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 IList ElementInViewList { get; } public ElementInView GetElementInView(int n) { if (n < 0) return null; @@ -2782,12 +2781,12 @@ public ElementInView GetElementInView(int n) public EntityTable ShapeInViewEntityTable { get; } - public IArray ShapeInViewShapeIndex { get; } + public IList ShapeInViewShapeIndex { get; } public int GetShapeInViewShapeIndex(int index) => ShapeInViewShapeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray ShapeInViewViewIndex { get; } + public IList 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 IList ShapeInViewList { get; } public ShapeInView GetShapeInView(int n) { if (n < 0) return null; @@ -2804,12 +2803,12 @@ public ShapeInView GetShapeInView(int n) public EntityTable AssetInViewEntityTable { get; } - public IArray AssetInViewAssetIndex { get; } + public IList AssetInViewAssetIndex { get; } public int GetAssetInViewAssetIndex(int index) => AssetInViewAssetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray AssetInViewViewIndex { get; } + public IList 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 IList AssetInViewList { get; } public AssetInView GetAssetInView(int n) { if (n < 0) return null; @@ -2826,12 +2825,12 @@ public AssetInView GetAssetInView(int n) public EntityTable AssetInViewSheetEntityTable { get; } - public IArray AssetInViewSheetAssetIndex { get; } + public IList AssetInViewSheetAssetIndex { get; } public int GetAssetInViewSheetAssetIndex(int index) => AssetInViewSheetAssetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray AssetInViewSheetViewSheetIndex { get; } + public IList 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 IList AssetInViewSheetList { get; } public AssetInViewSheet GetAssetInViewSheet(int n) { if (n < 0) return null; @@ -2848,24 +2847,24 @@ public AssetInViewSheet GetAssetInViewSheet(int n) public EntityTable LevelInViewEntityTable { get; } - public IArray LevelInViewExtents_Min_X { get; } + public IList 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 IList 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 IList 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 IList 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 IList 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 IList 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 IList LevelInViewLevelIndex { get; } public int GetLevelInViewLevelIndex(int index) => LevelInViewLevelIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IArray LevelInViewViewIndex { get; } + public IList 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 IList LevelInViewList { get; } public LevelInView GetLevelInView(int n) { if (n < 0) return null; @@ -2888,26 +2887,26 @@ public LevelInView GetLevelInView(int n) public EntityTable CameraEntityTable { get; } - public IArray CameraId { get; } + public IList CameraId { get; } public Int32 GetCameraId(int index, Int32 defaultValue = default) => CameraId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraIsPerspective { get; } + public IList CameraIsPerspective { get; } public Int32 GetCameraIsPerspective(int index, Int32 defaultValue = default) => CameraIsPerspective?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraVerticalExtent { get; } + public IList CameraVerticalExtent { get; } public Double GetCameraVerticalExtent(int index, Double defaultValue = default) => CameraVerticalExtent?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraHorizontalExtent { get; } + public IList CameraHorizontalExtent { get; } public Double GetCameraHorizontalExtent(int index, Double defaultValue = default) => CameraHorizontalExtent?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraFarDistance { get; } + public IList CameraFarDistance { get; } public Double GetCameraFarDistance(int index, Double defaultValue = default) => CameraFarDistance?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraNearDistance { get; } + public IList CameraNearDistance { get; } public Double GetCameraNearDistance(int index, Double defaultValue = default) => CameraNearDistance?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraTargetDistance { get; } + public IList CameraTargetDistance { get; } public Double GetCameraTargetDistance(int index, Double defaultValue = default) => CameraTargetDistance?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraRightOffset { get; } + public IList CameraRightOffset { get; } public Double GetCameraRightOffset(int index, Double defaultValue = default) => CameraRightOffset?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray CameraUpOffset { get; } + public IList 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 IList CameraList { get; } public Camera GetCamera(int n) { if (n < 0) return null; @@ -2931,41 +2930,41 @@ public Camera GetCamera(int n) public EntityTable MaterialEntityTable { get; } - public IArray MaterialName { get; } + public IList MaterialName { get; } public String GetMaterialName(int index, String defaultValue = "") => MaterialName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialMaterialCategory { get; } + public IList MaterialMaterialCategory { get; } public String GetMaterialMaterialCategory(int index, String defaultValue = "") => MaterialMaterialCategory?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColor_X { get; } + public IList MaterialColor_X { get; } public Double GetMaterialColor_X(int index, Double defaultValue = default) => MaterialColor_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColor_Y { get; } + public IList MaterialColor_Y { get; } public Double GetMaterialColor_Y(int index, Double defaultValue = default) => MaterialColor_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColor_Z { get; } + public IList MaterialColor_Z { get; } public Double GetMaterialColor_Z(int index, Double defaultValue = default) => MaterialColor_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColorUvScaling_X { get; } + public IList MaterialColorUvScaling_X { get; } public Double GetMaterialColorUvScaling_X(int index, Double defaultValue = default) => MaterialColorUvScaling_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColorUvScaling_Y { get; } + public IList MaterialColorUvScaling_Y { get; } public Double GetMaterialColorUvScaling_Y(int index, Double defaultValue = default) => MaterialColorUvScaling_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColorUvOffset_X { get; } + public IList MaterialColorUvOffset_X { get; } public Double GetMaterialColorUvOffset_X(int index, Double defaultValue = default) => MaterialColorUvOffset_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColorUvOffset_Y { get; } + public IList MaterialColorUvOffset_Y { get; } public Double GetMaterialColorUvOffset_Y(int index, Double defaultValue = default) => MaterialColorUvOffset_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialNormalUvScaling_X { get; } + public IList MaterialNormalUvScaling_X { get; } public Double GetMaterialNormalUvScaling_X(int index, Double defaultValue = default) => MaterialNormalUvScaling_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialNormalUvScaling_Y { get; } + public IList MaterialNormalUvScaling_Y { get; } public Double GetMaterialNormalUvScaling_Y(int index, Double defaultValue = default) => MaterialNormalUvScaling_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialNormalUvOffset_X { get; } + public IList MaterialNormalUvOffset_X { get; } public Double GetMaterialNormalUvOffset_X(int index, Double defaultValue = default) => MaterialNormalUvOffset_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialNormalUvOffset_Y { get; } + public IList MaterialNormalUvOffset_Y { get; } public Double GetMaterialNormalUvOffset_Y(int index, Double defaultValue = default) => MaterialNormalUvOffset_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialNormalAmount { get; } + public IList MaterialNormalAmount { get; } public Double GetMaterialNormalAmount(int index, Double defaultValue = default) => MaterialNormalAmount?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialGlossiness { get; } + public IList MaterialGlossiness { get; } public Double GetMaterialGlossiness(int index, Double defaultValue = default) => MaterialGlossiness?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialSmoothness { get; } + public IList MaterialSmoothness { get; } public Double GetMaterialSmoothness(int index, Double defaultValue = default) => MaterialSmoothness?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialTransparency { get; } + public IList MaterialTransparency { get; } public Double GetMaterialTransparency(int index, Double defaultValue = default) => MaterialTransparency?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IArray MaterialColorTextureFileIndex { get; } + public IList MaterialColorTextureFileIndex { get; } public int GetMaterialColorTextureFileIndex(int index) => MaterialColorTextureFileIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; public IList MaterialNormalTextureFileIndex { get; } public int GetMaterialNormalTextureFileIndex(int index) => MaterialNormalTextureFileIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; diff --git a/src/cs/vim/Vim.Format/ObjectModel/Validation.cs b/src/cs/vim/Vim.Format/ObjectModel/Validation.cs index e2a6afec..923dc0fd 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/Validation.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/Validation.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; -using Vim.LinqArray; using Vim.Math3d; using Vim.G3d; @@ -53,13 +52,13 @@ public ObjectModelValidationException(string message) : base(message) { } public static class Validation { - public static void ValidateBimDocument(this DocumentModel dm, ObjectModelValidationOptions validationOptions) + private static void ValidateBimDocument(this DocumentModel dm, ObjectModelValidationOptions validationOptions) { // There is at least one BimDocument in the document model. if (dm.NumBimDocument == 0 && validationOptions.BimDocumentMustExist) throw new ObjectModelValidationException($"No {nameof(BimDocument)} found."); - foreach (var bd in dm.BimDocumentList.ToEnumerable()) + foreach (var bd in dm.BimDocumentList) { var bdElement = bd.Element; if (bdElement == null) @@ -79,14 +78,14 @@ public static void ValidateBimDocument(this DocumentModel dm, ObjectModelValidat } } - public static void ValidateCompoundStructureLayer(this CompoundStructureLayer layer) + private static void ValidateCompoundStructureLayer(this CompoundStructureLayer layer) { // All CompoundLayers have a CompoundStructure if (layer.CompoundStructure == null) throw new ObjectModelValidationException($"{nameof(CompoundStructureLayer)} {layer.Index} has null {nameof(CompoundStructure)}"); } - public static void ValidateCompoundStructures(this DocumentModel dm) + private static void ValidateCompoundStructures(this DocumentModel dm) { var cslArray = dm.CompoundStructureLayerList.ToArray(); @@ -137,7 +136,7 @@ public static void ValidateCompoundStructures(this DocumentModel dm) throw new ObjectModelValidationException($"A {nameof(CompoundStructure)} must be referenced by exactly one {nameof(FamilyType)}."); } - public static void ValidateAssets(this DocumentModel dm) + private static void ValidateAssets(this DocumentModel dm) { // Validate that the assets contained in the buffers matches the assets entity table. var assetBuffers = dm.Document.Assets; @@ -149,9 +148,9 @@ public static void ValidateAssets(this DocumentModel dm) } } - public static void ValidateParameters(this DocumentModel dm) + private static void ValidateParameters(this DocumentModel dm) { - Parallel.ForEach(dm.ParameterList.ToEnumerable(), p => + Parallel.ForEach(dm.ParameterList, p => { // Each parameter must be associated to an element. if (p._Element.Index == EntityRelation.None) @@ -163,14 +162,14 @@ public static void ValidateParameters(this DocumentModel dm) }); // Validate the parameter descriptors. - foreach (var pd in dm.ParameterDescriptorList.ToEnumerable()) + foreach (var pd in dm.ParameterDescriptorList) { if (pd.DisplayUnit == null) throw new ObjectModelValidationException($"{nameof(DisplayUnit)} is null for {nameof(ParameterDescriptor)} {pd.Index}"); } } - public static void ValidatePhases(this DocumentModel dm) + private static void ValidatePhases(this DocumentModel dm) { // Validate the phase order information. var poArray = dm.PhaseOrderInBimDocumentList.ToArray(); @@ -199,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)}"); @@ -208,7 +207,7 @@ public static void ValidatePhases(this DocumentModel dm) /// /// Generic storage key check; ensures that the keys only appear once in the keySet. /// - public static void ValidateStorageKeys(IEnumerable storageKeyEntities) + private static void ValidateStorageKeys(IEnumerable storageKeyEntities) { var keySet = new HashSet(); foreach (var entity in storageKeyEntities) @@ -222,7 +221,7 @@ public static void ValidateStorageKeys(IEnumerable storageKeyEntiti /// /// Validate that the entities which inherit from IStorageKey all have unique storage key values. /// - public static void ValidateStorageKeys(this DocumentModel dm) + private static void ValidateStorageKeys(this DocumentModel dm) { // ReSharper disable once SuspiciousTypeConversion.Global var storageKeyEntityLists = dm.AllEntities @@ -233,7 +232,7 @@ public static void ValidateStorageKeys(this DocumentModel dm) } } - public static void ValidateMaterials(this DocumentModel dm) + private static void ValidateMaterials(this DocumentModel dm) { void ValidateDomain(string label, double value, double lowerInclusive, double upperInclusive, int index) { @@ -265,8 +264,8 @@ void ValidateDVector3Domain(string label, DVector3 value, DVector3 lowerInclusiv } } - public static Dictionary> GetViewToElementsMap( - this IArray elementInViewList) + private static Dictionary> GetViewToElementsMap( + this IList elementInViewList) => elementInViewList .Select(eiv => (viewIndex: eiv._View?.Index ?? -1, elementIndex: eiv._Element?.Index ?? -1)) .GroupBy(t => t.viewIndex) @@ -274,12 +273,12 @@ public static Dictionary> GetViewToElementsMap( g => g.Key, g => new HashSet(g.Select(t => t.elementIndex))); - public static void ValidateShapesInView(this DocumentModel dm) + private static void ValidateShapesInView(this DocumentModel dm) { var viewToElementsMap = dm.ElementInViewList.GetViewToElementsMap(); // 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; @@ -294,7 +293,7 @@ public static void ValidateShapesInView(this DocumentModel dm) } } - public static void ValidateEntitiesWithElement(this DocumentModel dm) + private static void ValidateEntitiesWithElement(this DocumentModel dm) { var entityWithElementTypes = new HashSet(ObjectModelReflection.GetEntityTypes() .Where(t => t.GetCustomAttributes(typeof(G3dAttributeReferenceAttribute)).Count() == 0)); @@ -303,8 +302,8 @@ public static void ValidateEntitiesWithElement(this DocumentModel dm) Parallel.ForEach(entityWithElementTypes, entityWithElementType => { - var elementIndices = ((IArray)dm.GetPropertyValue(entityWithElementType.Name + "ElementIndex")).ToArray(); - for (var i = 0; i < elementIndices.Length; ++i) + var elementIndices = (IList) dm.GetPropertyValue(entityWithElementType.Name + "ElementIndex"); + for (var i = 0; i < elementIndices.Count; ++i) { var elementIndex = elementIndices[i]; if (elementIndex < 0) @@ -315,7 +314,7 @@ public static void ValidateEntitiesWithElement(this DocumentModel dm) }); } - public static void ValidateElementInSystem(this DocumentModel dm) + private static void ValidateElementInSystem(this DocumentModel dm) { foreach (var eis in dm.ElementInSystemList) { From 9a178445941058b4be5515b45ead8480ba93aeec Mon Sep 17 00:00:00 2001 From: Vadym Vorotilin Date: Thu, 12 Dec 2024 18:26:55 +0200 Subject: [PATCH 09/22] ObjectModel refactoring fixes --- .../Vim.Gltf.Converter/VimToGltfStore.cs | 7 +- .../ObjectModelGenerator.cs | 7 +- .../ColumnExtensions.Buffer.cs | 2 +- src/cs/vim/Vim.Format.Core/EntityTable_v2.cs | 6 +- src/cs/vim/Vim.Format.Core/VimSchema.cs | 1 - .../ObjectModel/ObjectModelGenerated.cs | 115 +++++++++--------- 6 files changed, 67 insertions(+), 71 deletions(-) diff --git a/src/cs/samples/Vim.Gltf.Converter/VimToGltfStore.cs b/src/cs/samples/Vim.Gltf.Converter/VimToGltfStore.cs index b9882039..b4995bc2 100644 --- a/src/cs/samples/Vim.Gltf.Converter/VimToGltfStore.cs +++ b/src/cs/samples/Vim.Gltf.Converter/VimToGltfStore.cs @@ -3,9 +3,8 @@ using System.Diagnostics; using System.Linq; using System.Numerics; -using Vim.BFast; +using Vim.BFastLib; using Vim.G3d; -using Vim.LinqArray; namespace Vim.Gltf.Converter { @@ -26,7 +25,7 @@ public static void Convert(string vimFilePath, string gltfFilePath, float scale var gltfModel = ModelRoot.CreateModel(); var gltfScene = gltfModel.UseScene(0); - foreach (var vimMaterial in vim.Materials.ToEnumerable()) + foreach (var vimMaterial in vim.Materials) { var gltfMaterial = gltfModel.CreateMaterial(); gltfMaterial.InitializePBRMetallicRoughness(); @@ -56,7 +55,7 @@ public static void Convert(string vimFilePath, string gltfFilePath, float scale gltfVertexAccessor.SetVertexData(gltfVertexBufferView, 0, g3d.Vertices.Count); // Initialize a flat index buffer. - var vimIndexBufferBytes = g3d.Indices.ToEnumerable().Select(i => (uint)i).ToArray().ToBytes(); + var vimIndexBufferBytes = g3d.Indices.Select(i => (uint)i).ToArray().ToBytes(); var gltfIndexBufferView = gltfModel.UseBufferView(vimIndexBufferBytes); // Create the meshes and their primitives (submeshes) diff --git a/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs b/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs index 3c0d8fff..70c4529a 100644 --- a/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs +++ b/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs @@ -83,7 +83,7 @@ private static CodeBuilder WriteDocumentEntityData(Type t, CodeBuilder cb, Entit var dataColumnGetter = $"{t.Name}EntityTable?.{functionName}(\"{eci.SerializedValueColumnName}\")"; if (eci.EntityColumnAttribute.SerializedType != fieldType) { - dataColumnGetter += $"?.Select(v => ({fieldTypeName}) v)"; + dataColumnGetter += $"?.Select(v => ({fieldTypeName}) v).ToArray()"; } return dataColumnGetter; }).ToArray(); @@ -241,7 +241,7 @@ private static CodeBuilder WriteDocument(CodeBuilder cb) cb.AppendLine("// All entity collections"); cb.AppendLine("public Dictionary> AllEntities => new Dictionary>() {"); foreach (var t in entityTypes) - cb.AppendLine($"{{\"{t.GetEntityTableName()}\", {t.Name}List.ToEnumerable()}},"); + cb.AppendLine($"{{\"{t.GetEntityTableName()}\", {t.Name}List}},"); cb.AppendLine("};"); cb.AppendLine(); @@ -506,8 +506,7 @@ 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.Format.ObjectModel;"); - cb.AppendLine("using Vim.Util;"); + cb.AppendLine("using Vim.G3d;"); cb.AppendLine(); diff --git a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs index e2008830..7de080a0 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs @@ -199,7 +199,7 @@ public static SerializableEntityTable Concat( return concatenated; } - public static IList GetColumnValues(this INamedBuffer nb) where T : unmanaged + public static T[] GetColumnValues(this INamedBuffer nb) where T : unmanaged => nb.AsArray(); /// diff --git a/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs b/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs index a004c152..5ba63d55 100644 --- a/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs +++ b/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs @@ -58,7 +58,7 @@ private static T GetColumnOrDefault(Dictionary map, string key, T /// /// Returns the index column based on the given column name. /// - public IList GetIndexColumnValues(string columnName) + public int[] GetIndexColumnValues(string columnName) => GetColumnOrDefault(IndexColumns, columnName)?.GetColumnValues(); /// @@ -69,7 +69,7 @@ public string[] GetStringColumnValues(string columnName) var stringIndices = GetColumnOrDefault(StringColumns, columnName) ?.GetColumnValues() ?? Array.Empty(); - var strings = new string[stringIndices.Count]; + var strings = new string[stringIndices.Length]; for (var i = 0; i < strings.Length; i++) { @@ -84,7 +84,7 @@ public string[] GetStringColumnValues(string columnName) /// /// Returns the data column based on the given column name. /// - public IList GetDataColumnValues(string columnName) where T : unmanaged + public T[] GetDataColumnValues(string columnName) where T : unmanaged { var type = typeof(T); diff --git a/src/cs/vim/Vim.Format.Core/VimSchema.cs b/src/cs/vim/Vim.Format.Core/VimSchema.cs index e961448b..29ff3a72 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 { diff --git a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs index c7fabab8..0b7b0a15 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs @@ -5,8 +5,7 @@ using System.Collections.Generic; using System.Linq; using Vim.Math3d; -using Vim.Format.ObjectModel; -using Vim.Util; +using Vim.G3d; namespace Vim.Format.ObjectModel { // AUTO-GENERATED @@ -3735,60 +3734,60 @@ public Building GetBuilding(int n) // All entity collections public Dictionary> AllEntities => new Dictionary>() { - {"Vim.Asset", AssetList.ToEnumerable()}, - {"Vim.DisplayUnit", DisplayUnitList.ToEnumerable()}, - {"Vim.ParameterDescriptor", ParameterDescriptorList.ToEnumerable()}, - {"Vim.Parameter", ParameterList.ToEnumerable()}, - {"Vim.Element", ElementList.ToEnumerable()}, - {"Vim.Workset", WorksetList.ToEnumerable()}, - {"Vim.AssemblyInstance", AssemblyInstanceList.ToEnumerable()}, - {"Vim.Group", GroupList.ToEnumerable()}, - {"Vim.DesignOption", DesignOptionList.ToEnumerable()}, - {"Vim.Level", LevelList.ToEnumerable()}, - {"Vim.Phase", PhaseList.ToEnumerable()}, - {"Vim.Room", RoomList.ToEnumerable()}, - {"Vim.BimDocument", BimDocumentList.ToEnumerable()}, - {"Vim.DisplayUnitInBimDocument", DisplayUnitInBimDocumentList.ToEnumerable()}, - {"Vim.PhaseOrderInBimDocument", PhaseOrderInBimDocumentList.ToEnumerable()}, - {"Vim.Category", CategoryList.ToEnumerable()}, - {"Vim.Family", FamilyList.ToEnumerable()}, - {"Vim.FamilyType", FamilyTypeList.ToEnumerable()}, - {"Vim.FamilyInstance", FamilyInstanceList.ToEnumerable()}, - {"Vim.View", ViewList.ToEnumerable()}, - {"Vim.ElementInView", ElementInViewList.ToEnumerable()}, - {"Vim.ShapeInView", ShapeInViewList.ToEnumerable()}, - {"Vim.AssetInView", AssetInViewList.ToEnumerable()}, - {"Vim.AssetInViewSheet", AssetInViewSheetList.ToEnumerable()}, - {"Vim.LevelInView", LevelInViewList.ToEnumerable()}, - {"Vim.Camera", CameraList.ToEnumerable()}, - {"Vim.Material", MaterialList.ToEnumerable()}, - {"Vim.MaterialInElement", MaterialInElementList.ToEnumerable()}, - {"Vim.CompoundStructureLayer", CompoundStructureLayerList.ToEnumerable()}, - {"Vim.CompoundStructure", CompoundStructureList.ToEnumerable()}, - {"Vim.Node", NodeList.ToEnumerable()}, - {"Vim.Geometry", GeometryList.ToEnumerable()}, - {"Vim.Shape", ShapeList.ToEnumerable()}, - {"Vim.ShapeCollection", ShapeCollectionList.ToEnumerable()}, - {"Vim.ShapeInShapeCollection", ShapeInShapeCollectionList.ToEnumerable()}, - {"Vim.System", SystemList.ToEnumerable()}, - {"Vim.ElementInSystem", ElementInSystemList.ToEnumerable()}, - {"Vim.Warning", WarningList.ToEnumerable()}, - {"Vim.ElementInWarning", ElementInWarningList.ToEnumerable()}, - {"Vim.BasePoint", BasePointList.ToEnumerable()}, - {"Vim.PhaseFilter", PhaseFilterList.ToEnumerable()}, - {"Vim.Grid", GridList.ToEnumerable()}, - {"Vim.Area", AreaList.ToEnumerable()}, - {"Vim.AreaScheme", AreaSchemeList.ToEnumerable()}, - {"Vim.Schedule", ScheduleList.ToEnumerable()}, - {"Vim.ScheduleColumn", ScheduleColumnList.ToEnumerable()}, - {"Vim.ScheduleCell", ScheduleCellList.ToEnumerable()}, - {"Vim.ViewSheetSet", ViewSheetSetList.ToEnumerable()}, - {"Vim.ViewSheet", ViewSheetList.ToEnumerable()}, - {"Vim.ViewSheetInViewSheetSet", ViewSheetInViewSheetSetList.ToEnumerable()}, - {"Vim.ViewInViewSheetSet", ViewInViewSheetSetList.ToEnumerable()}, - {"Vim.ViewInViewSheet", ViewInViewSheetList.ToEnumerable()}, - {"Vim.Site", SiteList.ToEnumerable()}, - {"Vim.Building", BuildingList.ToEnumerable()}, + {"Vim.Asset", AssetList}, + {"Vim.DisplayUnit", DisplayUnitList}, + {"Vim.ParameterDescriptor", ParameterDescriptorList}, + {"Vim.Parameter", ParameterList}, + {"Vim.Element", ElementList}, + {"Vim.Workset", WorksetList}, + {"Vim.AssemblyInstance", AssemblyInstanceList}, + {"Vim.Group", GroupList}, + {"Vim.DesignOption", DesignOptionList}, + {"Vim.Level", LevelList}, + {"Vim.Phase", PhaseList}, + {"Vim.Room", RoomList}, + {"Vim.BimDocument", BimDocumentList}, + {"Vim.DisplayUnitInBimDocument", DisplayUnitInBimDocumentList}, + {"Vim.PhaseOrderInBimDocument", PhaseOrderInBimDocumentList}, + {"Vim.Category", CategoryList}, + {"Vim.Family", FamilyList}, + {"Vim.FamilyType", FamilyTypeList}, + {"Vim.FamilyInstance", FamilyInstanceList}, + {"Vim.View", ViewList}, + {"Vim.ElementInView", ElementInViewList}, + {"Vim.ShapeInView", ShapeInViewList}, + {"Vim.AssetInView", AssetInViewList}, + {"Vim.AssetInViewSheet", AssetInViewSheetList}, + {"Vim.LevelInView", LevelInViewList}, + {"Vim.Camera", CameraList}, + {"Vim.Material", MaterialList}, + {"Vim.MaterialInElement", MaterialInElementList}, + {"Vim.CompoundStructureLayer", CompoundStructureLayerList}, + {"Vim.CompoundStructure", CompoundStructureList}, + {"Vim.Node", NodeList}, + {"Vim.Geometry", GeometryList}, + {"Vim.Shape", ShapeList}, + {"Vim.ShapeCollection", ShapeCollectionList}, + {"Vim.ShapeInShapeCollection", ShapeInShapeCollectionList}, + {"Vim.System", SystemList}, + {"Vim.ElementInSystem", ElementInSystemList}, + {"Vim.Warning", WarningList}, + {"Vim.ElementInWarning", ElementInWarningList}, + {"Vim.BasePoint", BasePointList}, + {"Vim.PhaseFilter", PhaseFilterList}, + {"Vim.Grid", GridList}, + {"Vim.Area", AreaList}, + {"Vim.AreaScheme", AreaSchemeList}, + {"Vim.Schedule", ScheduleList}, + {"Vim.ScheduleColumn", ScheduleColumnList}, + {"Vim.ScheduleCell", ScheduleCellList}, + {"Vim.ViewSheetSet", ViewSheetSetList}, + {"Vim.ViewSheet", ViewSheetList}, + {"Vim.ViewSheetInViewSheetSet", ViewSheetInViewSheetSetList}, + {"Vim.ViewInViewSheetSet", ViewInViewSheetSetList}, + {"Vim.ViewInViewSheet", ViewInViewSheetList}, + {"Vim.Site", SiteList}, + {"Vim.Building", BuildingList}, }; // Entity types from table names @@ -3923,7 +3922,7 @@ public DocumentModel(Document d, bool inParallel = true) ParameterDescriptorGuid = ParameterDescriptorEntityTable?.GetStringColumnValues("string:Guid") ?? Array.Empty(); ParameterDescriptorStorageType = ParameterDescriptorEntityTable?.GetDataColumnValues("int:StorageType") ?? Array.Empty(); ParameterValue = ParameterEntityTable?.GetStringColumnValues("string:Value") ?? Array.Empty(); - ElementId = (ElementEntityTable?.GetDataColumnValues("long:Id") ?? ElementEntityTable?.GetDataColumnValues("int:Id")?.Select(v => (Int64) v)) ?? 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(); @@ -3986,7 +3985,7 @@ public DocumentModel(Document d, bool inParallel = true) 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)) ?? 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(); From 74eb01a982dfaf2f67d5519253468e2bfe90660c Mon Sep 17 00:00:00 2001 From: Vadym Vorotilin Date: Wed, 18 Dec 2024 12:57:12 +0200 Subject: [PATCH 10/22] G3dNext integrated instead of old G3D --- .../Vim.BFast/Buffers/BufferExtensions.cs | 10 + .../Vim.G3d.AssimpWrapper/AssimpExtensions.cs | 102 -- .../g3d/Vim.G3d.AssimpWrapper/AssimpLoader.cs | 36 - .../Vim.G3d.AssimpWrapper.csproj | 21 - .../CodeBuilder.cs | 2 +- .../Definitions.cs | 2 +- .../G3dBuffer.cs | 2 +- .../G3dCodeGen.cs | 17 +- .../G3dEntity.cs | 2 +- .../Program.cs | 2 +- .../Vim.G3d.CodeGen.csproj} | 4 +- src/cs/g3d/Vim.G3d.Tests/G3dTestUtils.cs | 33 + src/cs/g3d/Vim.G3d.Tests/G3dTests.cs | 339 ++----- .../Properties/Resources.Designer.cs | 73 -- .../Vim.G3d.Tests/Properties/Resources.resx | 124 --- src/cs/g3d/Vim.G3d.Tests/Resources/.gitignore | 1 - src/cs/g3d/Vim.G3d.Tests/Util.cs | 76 -- src/cs/g3d/Vim.G3d.Tests/Vim.G3d.Tests.csproj | 60 +- src/cs/g3d/Vim.G3d/AttributeDescriptor.cs | 158 ---- src/cs/g3d/Vim.G3d/AttributeExtensions.cs | 137 --- .../{Vim.G3dNext => Vim.G3d}/BufferMethods.cs | 2 +- src/cs/g3d/Vim.G3d/CommonAttributes.cs | 171 ---- src/cs/g3d/Vim.G3d/CommonAttributes.tt | 93 -- src/cs/g3d/Vim.G3d/Constants.cs | 86 ++ src/cs/g3d/Vim.G3d/Enums.cs | 98 -- src/cs/g3d/Vim.G3d/G3D.cs | 345 ------- src/cs/g3d/Vim.G3d/G3DBuilder.cs | 35 - .../g3d/{Vim.G3dNext => Vim.G3d}/G3dChunk.cs | 6 +- .../G3dGenerated.g.cs | 83 +- src/cs/g3d/Vim.G3d/G3dMaterial.cs | 20 - .../{Vim.G3dNext => Vim.G3d}/G3dMaterials.cs | 2 +- src/cs/g3d/Vim.G3d/G3dMesh.cs | 72 -- .../g3d/{Vim.G3dNext => Vim.G3d}/G3dScene.cs | 2 +- src/cs/g3d/Vim.G3d/G3dSerialization.cs | 33 - src/cs/g3d/Vim.G3d/G3dShape.cs | 23 - src/cs/g3d/{Vim.G3dNext => Vim.G3d}/G3dVim.cs | 50 +- src/cs/g3d/Vim.G3d/GeometryAttribute.cs | 243 ----- src/cs/g3d/Vim.G3d/GeometryAttributes.cs | 163 ---- .../Vim.G3d/GeometryAttributesExtensions.cs | 510 ---------- src/cs/g3d/Vim.G3d/Header.cs | 81 -- src/cs/g3d/Vim.G3d/IGeometryAttributes.cs | 22 - .../{Vim.G3dNext => Vim.G3d}/MetaHeader.cs | 2 +- src/cs/g3d/Vim.G3d/ObjExporter.cs | 59 -- src/cs/g3d/Vim.G3d/PlyExporter.cs | 78 -- src/cs/g3d/Vim.G3d/README.md | 109 --- src/cs/g3d/Vim.G3d/Validation.cs | 76 -- src/cs/g3d/Vim.G3d/Vim.G3d.csproj | 49 +- .../Vim.G3dNext.Tests.csproj | 37 - src/cs/g3d/Vim.G3dNext/Constants.cs | 47 - .../Vim.Gltf.Converter/VimToGltfStore.cs | 24 +- src/cs/samples/Vim.JsonDigest/MaterialInfo.cs | 1 - src/cs/samples/Vim.JsonDigest/RoomInfo.cs | 1 - .../Vim.Util}/ArrayExtensions.cs | 18 +- .../ObjectModelGenerator.cs | 16 +- .../ObjectModelTypeScriptGenerator.cs | 4 +- .../Vim.Format.CodeGen.csproj | 12 +- src/cs/vim/Vim.Format.Core/AssetInfo.cs | 43 +- src/cs/vim/Vim.Format.Core/BigG3dWriter.cs | 210 ----- .../ColumnExtensions.Buffer.cs | 48 +- .../ColumnExtensions.Reflection.cs | 6 +- .../vim/Vim.Format.Core/ColumnExtensions.cs | 15 +- src/cs/vim/Vim.Format.Core/ColumnInfo.cs | 10 +- src/cs/vim/Vim.Format.Core/Document.cs | 60 +- src/cs/vim/Vim.Format.Core/DocumentBuilder.cs | 13 +- .../DocumentBuilderExtensions.cs | 69 +- .../Vim.Format.Core/DocumentBuilderTypes.cs | 63 +- .../EntityColumnLoaderAttribute.cs | 2 +- src/cs/vim/Vim.Format.Core/EntityTable.cs | 74 +- .../vim/Vim.Format.Core/EntityTableBuilder.cs | 40 +- src/cs/vim/Vim.Format.Core/EntityTable_v2.cs | 2 +- src/cs/vim/Vim.Format.Core/G3dBuilder.cs | 37 +- .../vim/Vim.Format.Core/Geometry/Bounded.cs | 6 + .../Vim.Format.Core/Geometry/CatmullClark.cs | 229 +++++ .../Geometry/GeometryCuttingUtils.cs | 127 +++ src/cs/vim/Vim.Format.Core/Geometry/IMesh.cs | 24 - src/cs/vim/Vim.Format.Core/Geometry/IScene.cs | 27 - src/cs/vim/Vim.Format.Core/Geometry/KdTree.cs | 2 +- .../Geometry/MeshExtensions.cs | 170 ---- .../Geometry/MeshOptimization.cs | 27 +- .../Geometry/PerimeterProjection.cs | 595 ++++++++++++ .../Vim.Format.Core/Geometry/Primitives.cs | 236 +++-- .../vim/Vim.Format.Core/Geometry/QuadMesh.cs | 21 - .../Geometry/SceneExtensions.cs | 33 - .../vim/Vim.Format.Core/Geometry/TriMesh.cs | 20 - .../Vim.Format.Core/Geometry/Validation.cs | 66 +- .../Vim.Format.Core/Geometry/VimMaterial.cs | 21 - .../Geometry/VimMaterialNext.cs | 38 + .../vim/Vim.Format.Core/Geometry/VimMesh.cs | 444 +++++++++ .../Vim.Format.Core/Geometry/VimShapeNext.cs | 33 + .../Vim.Format.Core/SerializableDocument.cs | 76 +- .../SerializableEntityTable.cs | 101 +- .../SerializableHeaderExtensions.cs | 29 +- src/cs/vim/Vim.Format.Core/Serializer.cs | 250 ----- .../Vim.Format.Core/TypePrefixExtensions.cs | 22 - src/cs/vim/Vim.Format.Core/Validation.cs | 51 +- .../Vim.Format.Core/Vim.Format.Core.csproj | 13 +- src/cs/vim/Vim.Format.Core/VimConstants.cs | 10 + src/cs/vim/Vim.Format.Core/VimSchema.cs | 9 +- .../Vim.Format.Tests/EntityTable_v2_Tests.cs | 1 - src/cs/vim/Vim.Format.Tests/FormatTests.cs | 35 +- .../Geometry/GeometryTests.cs | 314 +++---- .../Geometry/PerimeterTest.cs | 27 + .../SerializableDocumentTests.cs | 174 ++++ .../vim/Vim.Format.Tests/TrainingSetTests.cs | 1 - .../Vim.Format.Tests/Vim.Format.Tests.csproj | 11 +- .../Vim.Format.Vimx.Conversion/Chunking.cs | 198 ++++ .../Vim.Format.Vimx.Conversion/Ordering.cs | 78 ++ .../Vim.Format.Vimx.Conversion.csproj | 19 + .../VimxConverter.cs | 224 +++++ .../Vim.Format.Vimx/Vim.Format.Vimx.csproj} | 7 +- src/cs/vim/Vim.Format.Vimx/Vimx.cs | 77 ++ src/cs/vim/Vim.Format.Vimx/VimxHeader.cs | 35 + src/cs/vim/Vim.Format/Merge/MergeService.cs | 18 +- .../Vim.Format/Merge/MergedTableBuilder.cs | 32 +- .../Merge/RemappedEntityTableBuilder.cs | 2 +- .../ObjectModel/ElementIndexMaps.cs | 5 +- .../vim/Vim.Format/ObjectModel/ElementInfo.cs | 11 +- .../ObjectModel/ObjectModelExtensions.cs | 20 +- .../ObjectModel/ObjectModelGenerated.cs | 874 +++++++++--------- .../vim/Vim.Format/ObjectModel/Validation.cs | 36 +- .../Vim.Format/SceneBuilder/ILoadingStep.cs | 21 + .../SceneBuilder/LoadingProgress.cs | 22 + .../SceneBuilder/LoadingSequence.cs | 22 + .../Vim.Format/SceneBuilder/LoadingStep.cs | 24 + .../vim/Vim.Format/SceneBuilder/Validation.cs | 87 +- .../vim/Vim.Format/SceneBuilder/VimScene.cs | 91 +- .../SceneBuilder/VimSceneHelpers.cs | 11 +- .../Vim.Format/SceneBuilder/VimSceneNode.cs | 45 +- .../vim/Vim.Format/SceneBuilder/VimShape.cs | 25 - src/cs/vim/Vim.Format/Vim.Format.csproj | 17 +- .../Vim.Vimx.Test/Vim.Format.Vimx.Test.csproj | 32 + src/cs/vim/Vim.Vimx.Test/VimxActions.cs | 30 + src/cs/vim/Vim.Vimx.Test/VimxTests.cs | 30 + src/ts/src/objectModel.ts | 4 +- 134 files changed, 4306 insertions(+), 5600 deletions(-) delete mode 100644 src/cs/g3d/Vim.G3d.AssimpWrapper/AssimpExtensions.cs delete mode 100644 src/cs/g3d/Vim.G3d.AssimpWrapper/AssimpLoader.cs delete mode 100644 src/cs/g3d/Vim.G3d.AssimpWrapper/Vim.G3d.AssimpWrapper.csproj rename src/cs/g3d/{Vim.G3dNext.CodeGen => Vim.G3d.CodeGen}/CodeBuilder.cs (98%) rename src/cs/g3d/{Vim.G3dNext.CodeGen => Vim.G3d.CodeGen}/Definitions.cs (99%) rename src/cs/g3d/{Vim.G3dNext.CodeGen => Vim.G3d.CodeGen}/G3dBuffer.cs (97%) rename src/cs/g3d/{Vim.G3dNext.CodeGen => Vim.G3d.CodeGen}/G3dCodeGen.cs (89%) rename src/cs/g3d/{Vim.G3dNext.CodeGen => Vim.G3d.CodeGen}/G3dEntity.cs (96%) rename src/cs/g3d/{Vim.G3dNext.CodeGen => Vim.G3d.CodeGen}/Program.cs (85%) rename src/cs/g3d/{Vim.G3dNext.CodeGen/Vim.G3dNext.CodeGen.csproj => Vim.G3d.CodeGen/Vim.G3d.CodeGen.csproj} (79%) create mode 100644 src/cs/g3d/Vim.G3d.Tests/G3dTestUtils.cs delete mode 100644 src/cs/g3d/Vim.G3d.Tests/Properties/Resources.Designer.cs delete mode 100644 src/cs/g3d/Vim.G3d.Tests/Properties/Resources.resx delete mode 100644 src/cs/g3d/Vim.G3d.Tests/Resources/.gitignore delete mode 100644 src/cs/g3d/Vim.G3d.Tests/Util.cs delete mode 100644 src/cs/g3d/Vim.G3d/AttributeDescriptor.cs delete mode 100644 src/cs/g3d/Vim.G3d/AttributeExtensions.cs rename src/cs/g3d/{Vim.G3dNext => Vim.G3d}/BufferMethods.cs (98%) delete mode 100644 src/cs/g3d/Vim.G3d/CommonAttributes.cs delete mode 100644 src/cs/g3d/Vim.G3d/CommonAttributes.tt create mode 100644 src/cs/g3d/Vim.G3d/Constants.cs delete mode 100644 src/cs/g3d/Vim.G3d/Enums.cs delete mode 100644 src/cs/g3d/Vim.G3d/G3D.cs delete mode 100644 src/cs/g3d/Vim.G3d/G3DBuilder.cs rename src/cs/g3d/{Vim.G3dNext => Vim.G3d}/G3dChunk.cs (99%) rename src/cs/g3d/{Vim.G3dNext => Vim.G3d}/G3dGenerated.g.cs (83%) delete mode 100644 src/cs/g3d/Vim.G3d/G3dMaterial.cs rename src/cs/g3d/{Vim.G3dNext => Vim.G3d}/G3dMaterials.cs (94%) delete mode 100644 src/cs/g3d/Vim.G3d/G3dMesh.cs rename src/cs/g3d/{Vim.G3dNext => Vim.G3d}/G3dScene.cs (90%) delete mode 100644 src/cs/g3d/Vim.G3d/G3dSerialization.cs delete mode 100644 src/cs/g3d/Vim.G3d/G3dShape.cs rename src/cs/g3d/{Vim.G3dNext => Vim.G3d}/G3dVim.cs (79%) delete mode 100644 src/cs/g3d/Vim.G3d/GeometryAttribute.cs delete mode 100644 src/cs/g3d/Vim.G3d/GeometryAttributes.cs delete mode 100644 src/cs/g3d/Vim.G3d/GeometryAttributesExtensions.cs delete mode 100644 src/cs/g3d/Vim.G3d/Header.cs delete mode 100644 src/cs/g3d/Vim.G3d/IGeometryAttributes.cs rename src/cs/g3d/{Vim.G3dNext => Vim.G3d}/MetaHeader.cs (99%) delete mode 100644 src/cs/g3d/Vim.G3d/ObjExporter.cs delete mode 100644 src/cs/g3d/Vim.G3d/PlyExporter.cs delete mode 100644 src/cs/g3d/Vim.G3d/README.md delete mode 100644 src/cs/g3d/Vim.G3d/Validation.cs delete mode 100644 src/cs/g3d/Vim.G3dNext.Tests/Vim.G3dNext.Tests.csproj delete mode 100644 src/cs/g3d/Vim.G3dNext/Constants.cs rename src/cs/{g3d/Vim.G3d => util/Vim.Util}/ArrayExtensions.cs (89%) delete mode 100644 src/cs/vim/Vim.Format.Core/BigG3dWriter.cs create mode 100644 src/cs/vim/Vim.Format.Core/Geometry/CatmullClark.cs create mode 100644 src/cs/vim/Vim.Format.Core/Geometry/GeometryCuttingUtils.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/IMesh.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/IScene.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/MeshExtensions.cs create mode 100644 src/cs/vim/Vim.Format.Core/Geometry/PerimeterProjection.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/QuadMesh.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/SceneExtensions.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/TriMesh.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Geometry/VimMaterial.cs create mode 100644 src/cs/vim/Vim.Format.Core/Geometry/VimMaterialNext.cs create mode 100644 src/cs/vim/Vim.Format.Core/Geometry/VimMesh.cs create mode 100644 src/cs/vim/Vim.Format.Core/Geometry/VimShapeNext.cs delete mode 100644 src/cs/vim/Vim.Format.Core/Serializer.cs delete mode 100644 src/cs/vim/Vim.Format.Core/TypePrefixExtensions.cs create mode 100644 src/cs/vim/Vim.Format.Tests/Geometry/PerimeterTest.cs create mode 100644 src/cs/vim/Vim.Format.Tests/SerializableDocumentTests.cs create mode 100644 src/cs/vim/Vim.Format.Vimx.Conversion/Chunking.cs create mode 100644 src/cs/vim/Vim.Format.Vimx.Conversion/Ordering.cs create mode 100644 src/cs/vim/Vim.Format.Vimx.Conversion/Vim.Format.Vimx.Conversion.csproj create mode 100644 src/cs/vim/Vim.Format.Vimx.Conversion/VimxConverter.cs rename src/cs/{g3d/Vim.G3dNext/Vim.G3dNext.csproj => vim/Vim.Format.Vimx/Vim.Format.Vimx.csproj} (65%) create mode 100644 src/cs/vim/Vim.Format.Vimx/Vimx.cs create mode 100644 src/cs/vim/Vim.Format.Vimx/VimxHeader.cs create mode 100644 src/cs/vim/Vim.Format/SceneBuilder/ILoadingStep.cs create mode 100644 src/cs/vim/Vim.Format/SceneBuilder/LoadingProgress.cs create mode 100644 src/cs/vim/Vim.Format/SceneBuilder/LoadingSequence.cs create mode 100644 src/cs/vim/Vim.Format/SceneBuilder/LoadingStep.cs delete mode 100644 src/cs/vim/Vim.Format/SceneBuilder/VimShape.cs create mode 100644 src/cs/vim/Vim.Vimx.Test/Vim.Format.Vimx.Test.csproj create mode 100644 src/cs/vim/Vim.Vimx.Test/VimxActions.cs create mode 100644 src/cs/vim/Vim.Vimx.Test/VimxTests.cs diff --git a/src/cs/bfast/Vim.BFast/Buffers/BufferExtensions.cs b/src/cs/bfast/Vim.BFast/Buffers/BufferExtensions.cs index 6260c22b..91fd57c4 100644 --- a/src/cs/bfast/Vim.BFast/Buffers/BufferExtensions.cs +++ b/src/cs/bfast/Vim.BFast/Buffers/BufferExtensions.cs @@ -86,5 +86,15 @@ public static Buffer ReadBuffer(this Stream stream, int numBytes) public static void Write(this Stream stream, IBuffer buffer) => buffer.Write(stream); + + public static NamedBuffer Fill(this NamedBuffer buffer, T value) where T : unmanaged + { + var array = new T[buffer.Data.Length]; + for (var i = 0; i < array.Length; i++) + { + array[i] = value; + } + return array.ToNamedBuffer(buffer.Name); + } } } diff --git a/src/cs/g3d/Vim.G3d.AssimpWrapper/AssimpExtensions.cs b/src/cs/g3d/Vim.G3d.AssimpWrapper/AssimpExtensions.cs deleted file mode 100644 index 3d025d24..00000000 --- a/src/cs/g3d/Vim.G3d.AssimpWrapper/AssimpExtensions.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Linq; -using Assimp; -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()).ToArray(); - var nodes = scene.GetNodes().ToArray(); - if (nodes.Length == 0 || nodes.Length == 1) - return meshes.Length > 0 ? meshes[0] : G3D.Empty; - - var mergedAttributes = meshes.Select(m => (IGeometryAttributes) m).ToArray().Merge().Attributes.ToList(); - - var subGeoTransforms = nodes.Select(n => n.Transform.ToMath3D()).ToArray().ToInstanceTransformAttribute(); - mergedAttributes.Add(subGeoTransforms); - - var meshIndices = nodes.Select(n => n.MeshIndex).ToArray().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.ToArray().Select(ToMath3D).ToArray()); - bldr.AddIndices(indices); - - if (mesh.HasTangentBasis) - bldr.Add(mesh.BiTangents.Select(ToMath3D).ToArray().ToVertexBitangentAttribute()); - - if (mesh.HasTangentBasis) - bldr.Add(mesh.Tangents.Select(x => ToMath3D(x).ToVector4()).ToArray().ToVertexTangentAttribute()); - - if (mesh.HasNormals) - bldr.Add(mesh.Normals.Select(ToMath3D).ToArray().ToVertexNormalAttribute()); - - for (var i = 0; i < mesh.TextureCoordinateChannelCount; ++i) - { - var uvChannel = mesh.TextureCoordinateChannels[i]; - bldr.Add(uvChannel.Select(ToMath3D).ToArray().ToVertexUvwAttribute(i)); - } - - for (var i = 0; i < mesh.VertexColorChannelCount; ++i) - { - var vcChannel = mesh.VertexColorChannels[i]; - bldr.Add(vcChannel.Select(ToMath3D).ToArray().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 3b567b9f..00000000 --- a/src/cs/g3d/Vim.G3d.AssimpWrapper/Vim.G3d.AssimpWrapper.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - netstandard2.0 - Vim.G3d.AssimpWrapper - - - - - - - - - - - - True - - - - diff --git a/src/cs/g3d/Vim.G3dNext.CodeGen/CodeBuilder.cs b/src/cs/g3d/Vim.G3d.CodeGen/CodeBuilder.cs similarity index 98% rename from src/cs/g3d/Vim.G3dNext.CodeGen/CodeBuilder.cs rename to src/cs/g3d/Vim.G3d.CodeGen/CodeBuilder.cs index 8dd3283a..41013d8f 100644 --- a/src/cs/g3d/Vim.G3dNext.CodeGen/CodeBuilder.cs +++ b/src/cs/g3d/Vim.G3d.CodeGen/CodeBuilder.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Text; -namespace Vim.G3dNext.CodeGen +namespace Vim.G3d.CodeGen { public class CodeBuilder { diff --git a/src/cs/g3d/Vim.G3dNext.CodeGen/Definitions.cs b/src/cs/g3d/Vim.G3d.CodeGen/Definitions.cs similarity index 99% rename from src/cs/g3d/Vim.G3dNext.CodeGen/Definitions.cs rename to src/cs/g3d/Vim.G3d.CodeGen/Definitions.cs index 9eb47307..8d53e40b 100644 --- a/src/cs/g3d/Vim.G3dNext.CodeGen/Definitions.cs +++ b/src/cs/g3d/Vim.G3d.CodeGen/Definitions.cs @@ -1,6 +1,6 @@ using Vim.Math3d; -namespace Vim.G3dNext.CodeGen +namespace Vim.G3d.CodeGen { internal class Definitions { diff --git a/src/cs/g3d/Vim.G3dNext.CodeGen/G3dBuffer.cs b/src/cs/g3d/Vim.G3d.CodeGen/G3dBuffer.cs similarity index 97% rename from src/cs/g3d/Vim.G3dNext.CodeGen/G3dBuffer.cs rename to src/cs/g3d/Vim.G3d.CodeGen/G3dBuffer.cs index 97cf4ce1..e3763042 100644 --- a/src/cs/g3d/Vim.G3dNext.CodeGen/G3dBuffer.cs +++ b/src/cs/g3d/Vim.G3d.CodeGen/G3dBuffer.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics; -namespace Vim.G3dNext.CodeGen +namespace Vim.G3d.CodeGen { public enum BufferType { diff --git a/src/cs/g3d/Vim.G3dNext.CodeGen/G3dCodeGen.cs b/src/cs/g3d/Vim.G3d.CodeGen/G3dCodeGen.cs similarity index 89% rename from src/cs/g3d/Vim.G3dNext.CodeGen/G3dCodeGen.cs rename to src/cs/g3d/Vim.G3d.CodeGen/G3dCodeGen.cs index 5724b22f..773342cc 100644 --- a/src/cs/g3d/Vim.G3dNext.CodeGen/G3dCodeGen.cs +++ b/src/cs/g3d/Vim.G3d.CodeGen/G3dCodeGen.cs @@ -2,7 +2,7 @@ using System.IO; using System.Linq; -namespace Vim.G3dNext.CodeGen +namespace Vim.G3d.CodeGen { public static class G3dCodeGen { @@ -16,7 +16,7 @@ public static void WriteDocument(string filePath) cb.AppendLine("// ReSharper disable All"); cb.AppendLine("using Vim.BFastLib;"); cb.AppendLine(); - cb.AppendLine("namespace Vim.G3dNext"); + cb.AppendLine("namespace Vim.G3d"); cb.AppendLine("{"); WriteEntities(cb); cb.AppendLine("}"); @@ -85,6 +85,11 @@ public BFast ToBFast() 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 => @@ -113,6 +118,14 @@ public bool Equals({entity.ClassName} other ) ); }} + 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. diff --git a/src/cs/g3d/Vim.G3dNext.CodeGen/G3dEntity.cs b/src/cs/g3d/Vim.G3d.CodeGen/G3dEntity.cs similarity index 96% rename from src/cs/g3d/Vim.G3dNext.CodeGen/G3dEntity.cs rename to src/cs/g3d/Vim.G3d.CodeGen/G3dEntity.cs index 2be5f9e7..5c224c31 100644 --- a/src/cs/g3d/Vim.G3dNext.CodeGen/G3dEntity.cs +++ b/src/cs/g3d/Vim.G3d.CodeGen/G3dEntity.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Vim.G3dNext.CodeGen +namespace Vim.G3d.CodeGen { /// /// Holds the data to generate the code for a g3d entity. diff --git a/src/cs/g3d/Vim.G3dNext.CodeGen/Program.cs b/src/cs/g3d/Vim.G3d.CodeGen/Program.cs similarity index 85% rename from src/cs/g3d/Vim.G3dNext.CodeGen/Program.cs rename to src/cs/g3d/Vim.G3d.CodeGen/Program.cs index 5470fb2f..6bf7d644 100644 --- a/src/cs/g3d/Vim.G3dNext.CodeGen/Program.cs +++ b/src/cs/g3d/Vim.G3d.CodeGen/Program.cs @@ -1,4 +1,4 @@ -namespace Vim.G3dNext.CodeGen +namespace Vim.G3d.CodeGen { public static class Program { diff --git a/src/cs/g3d/Vim.G3dNext.CodeGen/Vim.G3dNext.CodeGen.csproj b/src/cs/g3d/Vim.G3d.CodeGen/Vim.G3d.CodeGen.csproj similarity index 79% rename from src/cs/g3d/Vim.G3dNext.CodeGen/Vim.G3dNext.CodeGen.csproj rename to src/cs/g3d/Vim.G3d.CodeGen/Vim.G3d.CodeGen.csproj index 41239fb1..74a5126c 100644 --- a/src/cs/g3d/Vim.G3dNext.CodeGen/Vim.G3dNext.CodeGen.csproj +++ b/src/cs/g3d/Vim.G3d.CodeGen/Vim.G3d.CodeGen.csproj @@ -7,7 +7,7 @@ Exe - Vim.G3dNext.CodeGen.Program + Vim.G3d.CodeGen.Program @@ -15,7 +15,7 @@ - + diff --git a/src/cs/g3d/Vim.G3d.Tests/G3dTestUtils.cs b/src/cs/g3d/Vim.G3d.Tests/G3dTestUtils.cs new file mode 100644 index 00000000..9bd3b033 --- /dev/null +++ b/src/cs/g3d/Vim.G3d.Tests/G3dTestUtils.cs @@ -0,0 +1,33 @@ +using Vim.Math3d; + +namespace Vim.G3d.Tests +{ + public static class G3dTestUtils + { + public static G3dVim CreateTestG3d() + { + 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; + } + } +} diff --git a/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs b/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs index 393a694a..5e2dd0e9 100644 --- a/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs +++ b/src/cs/g3d/Vim.G3d.Tests/G3dTests.cs @@ -1,314 +1,83 @@ -using Assimp; using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; +using NUnit.Framework.Internal; using Vim.BFastLib; -using Vim.G3d.AssimpWrapper; -using Vim.Math3d; +using Vim.Util.Tests; namespace Vim.G3d.Tests { [TestFixture] - public static class G3dTests + 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"); - - [SetUp] - public static void Setup() - { - if (!Directory.Exists(RootFolder)) - { - Directory.CreateDirectory(RootFolder); - } - if (!Directory.Exists(TestInputFolder)) - { - Directory.CreateDirectory(TestInputFolder); - } - if (!Directory.Exists(TestOutputFolder)) - { - Directory.CreateDirectory(TestOutputFolder); - } - } + private static readonly string ResidencePath = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); - 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) + [Test] + public static void Can_Read_G3d_From_Vim() { - 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"); - } + var g3d = G3dVim.FromVim(ResidencePath); + Assert.IsNotNull(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_Ignore_Extra_Attributes() { - 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.ToBFast().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"); + // 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 TriangleTest() + 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) - }; - - var indices = new[] { 0, 1, 2 }; - var materialIndices = new[] { 5 }; - - var g3d = new G3DBuilder() - .AddVertices(vertices) - .AddIndices(indices) - .Add(materialIndices.ToFaceMaterialAttribute()) - .ToG3D(); - - var bfast = g3d.ToBFast(); - var g = G3D.Read(bfast); - - 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()); + var expected = G3dTestUtils.CreateTestG3d(); + var g3d = new G3dVim(expected.ToBFast()); + Assert.IsTrue(g3d.Equals(expected)); } - [Test] - public static void UnexpectedAttributes_Are_Ignored() - { - var bfast = new BFast(); - bfast.SetArray("g3d:instance:potato:0:int32:1", new int[] { 5 }); - var g = G3D.Read(bfast); - var parsedInstanceAttrs = g.Attributes.Where(ga => ga.Descriptor.Association == Association.assoc_instance).ToArray(); - 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 expectedG3d = bldr.ToG3D(); - Assert.AreEqual(nVerts, expectedG3d.NumVertices); - var bfast = expectedG3d.ToBFast(); - var resultG3d = G3D.Read(bfast); - ValidateSameG3D(expectedG3d, resultG3d); - } - - [Test, Explicit] - [Platform(Exclude = "Linux,Unix", Reason = "AssimpNet is failing to load its dependency on 'libdl.so'.")] - public static void TestWriters() + [Test] + 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 dba826c3..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", "17.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:\Users\Rober\Desktop\Vim\vim-format\src\cs\g3d\Vim.G3d.Tests\ - ///. - /// - internal static string ProjDir { - get { - return ResourceManager.GetString("ProjDir", resourceCulture); - } - } - } -} diff --git a/src/cs/g3d/Vim.G3d.Tests/Properties/Resources.resx b/src/cs/g3d/Vim.G3d.Tests/Properties/Resources.resx deleted file mode 100644 index 579dd3b6..00000000 --- a/src/cs/g3d/Vim.G3d.Tests/Properties/Resources.resx +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - ..\Resources\ProjDir.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 - - \ No newline at end of file diff --git a/src/cs/g3d/Vim.G3d.Tests/Resources/.gitignore b/src/cs/g3d/Vim.G3d.Tests/Resources/.gitignore deleted file mode 100644 index d8fff56a..00000000 --- a/src/cs/g3d/Vim.G3d.Tests/Resources/.gitignore +++ /dev/null @@ -1 +0,0 @@ -ProjDir.txt \ No newline at end of file diff --git a/src/cs/g3d/Vim.G3d.Tests/Util.cs b/src/cs/g3d/Vim.G3d.Tests/Util.cs deleted file mode 100644 index 2ff62684..00000000 --- a/src/cs/g3d/Vim.G3d.Tests/Util.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; - -namespace Vim.G3d.Tests -{ - public static class Util - { - public static (long, long) GetMemoryConsumptionAndMSecElapsed(Action action) - { - var time = 0L; - var mem = GetMemoryConsumption( - () => time = GetMSecElapsed(action)); - return (mem, time); - } - - public static long GetMSecElapsed(Action action) - { - var sw = Stopwatch.StartNew(); - action(); - return sw.ElapsedMilliseconds; - } - - /// - /// Creates a directory if needed, or clears all of its contents otherwise - /// - public static string CreateAndClearDirectory(string dirPath) - { - if (!Directory.Exists(dirPath)) - Directory.CreateDirectory(dirPath); - else - DeleteFolderContents(dirPath); - return dirPath; - } - - /// - /// Deletes all contents in a folder - /// - /// - /// https://stackoverflow.com/questions/1288718/how-to-delete-all-files-and-folders-in-a-directory - /// - private static void DeleteFolderContents(string folderPath) - { - var di = new DirectoryInfo(folderPath); - foreach (var dir in di.EnumerateDirectories().AsParallel()) - DeleteFolderAndAllContents(dir.FullName); - foreach (var file in di.EnumerateFiles().AsParallel()) - file.Delete(); - } - - /// - /// Deletes everything in a folder and then the folder. - /// - private static void DeleteFolderAndAllContents(string folderPath) - { - if (!Directory.Exists(folderPath)) - return; - - DeleteFolderContents(folderPath); - Directory.Delete(folderPath); - } - - // NOTE: Calling a function generates additional memory - private static long GetMemoryConsumption(Action action) - { - GC.Collect(); - GC.WaitForPendingFinalizers(); - var memBefore = GC.GetTotalMemory(true); - action(); - GC.Collect(); - GC.WaitForPendingFinalizers(); - return GC.GetTotalMemory(true) - memBefore; - } - } -} \ No newline at end of file diff --git a/src/cs/g3d/Vim.G3d.Tests/Vim.G3d.Tests.csproj b/src/cs/g3d/Vim.G3d.Tests/Vim.G3d.Tests.csproj index ae2d33e9..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,45 +1,29 @@  - - net6.0 - false - + + net6.0 + enable + enable - - - - - + false + true + - - - - - - + + + + + - - - True - True - Resources.resx - - + + + + + + + True + + + - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - - - - - - True - - - 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 567bbd89..00000000 --- a/src/cs/g3d/Vim.G3d/AttributeExtensions.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -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, AttributeDescriptor desc) where T : unmanaged - => new GeometryAttribute(self, desc); - - public static GeometryAttribute ToAttribute(this IList self, string desc) where T : unmanaged - => self.ToAttribute(AttributeDescriptor.Parse(desc)); - - public static GeometryAttribute ToAttribute(this IList self, string desc, int index) where T : unmanaged - => self.ToAttribute(AttributeDescriptor.Parse(desc).SetIndex(index)); - - public static IList 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)).ToArray(); - if (desc.DataArity == 2) - return attr.AsType().Data.Select(vc => new Vector4(vc.X, vc.Y, 0, 1f)).ToArray(); - if (desc.DataArity == 1) - return attr.AsType().Data.Select(vc => new Vector4(vc, vc, vc, 1f)).ToArray(); - } - 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)).ToArray(); - if (desc.DataArity == 3) - return attr.AsType().Data.Select(b => new Vector4(b.X / 255f, b.Y / 255f, b.Z / 255f, 1f)).ToArray(); - if (desc.DataArity == 2) - return attr.AsType().Data.Select(b => new Vector4(b.X / 255f, b.Y / 255f, 0f, 1f)).ToArray(); - if (desc.DataArity == 1) - return attr.AsType().Data.Select(b => new Vector4(b / 255f, b / 255f, b / 255f, 1f)).ToArray(); - } - 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; - } -} diff --git a/src/cs/g3d/Vim.G3dNext/BufferMethods.cs b/src/cs/g3d/Vim.G3d/BufferMethods.cs similarity index 98% rename from src/cs/g3d/Vim.G3dNext/BufferMethods.cs rename to src/cs/g3d/Vim.G3d/BufferMethods.cs index e4f74d70..c7330f7e 100644 --- a/src/cs/g3d/Vim.G3dNext/BufferMethods.cs +++ b/src/cs/g3d/Vim.G3d/BufferMethods.cs @@ -2,7 +2,7 @@ using System.IO; using System.Linq; -namespace Vim.G3dNext +namespace Vim.G3d { public static class BufferMethods { diff --git a/src/cs/g3d/Vim.G3d/CommonAttributes.cs b/src/cs/g3d/Vim.G3d/CommonAttributes.cs deleted file mode 100644 index 461c6e91..00000000 --- a/src/cs/g3d/Vim.G3d/CommonAttributes.cs +++ /dev/null @@ -1,171 +0,0 @@ - -// AUTOGENERATED FILE: DO NOT EDIT -// This file is generated from CommonAttributeExtensions.tt - - -using System.Collections.Generic; -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 IList xs, int index) => xs.ToAttribute(CommonAttributes.ObjectFaceSize, index); - public static GeometryAttribute ToObjectFaceSizeAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.ObjectFaceSize); - public static GeometryAttribute GetAttributeObjectFaceSize(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.ObjectFaceSize); - public static IList GetAttributeDataObjectFaceSize(this IGeometryAttributes self) => self.GetAttributeObjectFaceSize()?.Data; - public static GeometryAttribute ToIndexAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.Index, index); - public static GeometryAttribute ToIndexAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.Index); - public static GeometryAttribute GetAttributeIndex(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.Index); - public static IList GetAttributeDataIndex(this IGeometryAttributes self) => self.GetAttributeIndex()?.Data; - public static GeometryAttribute ToPositionAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.Position, index); - public static GeometryAttribute ToPositionAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.Position); - public static GeometryAttribute GetAttributePosition(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.Position); - public static IList GetAttributeDataPosition(this IGeometryAttributes self) => self.GetAttributePosition()?.Data; - public static GeometryAttribute ToVertexUvAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexUv, index); - public static GeometryAttribute ToVertexUvAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexUv); - public static GeometryAttribute GetAttributeVertexUv(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexUv); - public static IList GetAttributeDataVertexUv(this IGeometryAttributes self) => self.GetAttributeVertexUv()?.Data; - public static GeometryAttribute ToVertexUvwAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexUvw, index); - public static GeometryAttribute ToVertexUvwAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexUvw); - public static GeometryAttribute GetAttributeVertexUvw(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexUvw); - public static IList GetAttributeDataVertexUvw(this IGeometryAttributes self) => self.GetAttributeVertexUvw()?.Data; - public static GeometryAttribute ToVertexNormalAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexNormal, index); - public static GeometryAttribute ToVertexNormalAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexNormal); - public static GeometryAttribute GetAttributeVertexNormal(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexNormal); - public static IList GetAttributeDataVertexNormal(this IGeometryAttributes self) => self.GetAttributeVertexNormal()?.Data; - public static GeometryAttribute ToVertexColorAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexColor, index); - public static GeometryAttribute ToVertexColorAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexColor); - public static GeometryAttribute GetAttributeVertexColor(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexColor); - public static IList GetAttributeDataVertexColor(this IGeometryAttributes self) => self.GetAttributeVertexColor()?.Data; - public static GeometryAttribute ToVertexColor8BitAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexColor8Bit, index); - public static GeometryAttribute ToVertexColor8BitAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexColor8Bit); - public static GeometryAttribute GetAttributeVertexColor8Bit(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexColor8Bit); - public static IList GetAttributeDataVertexColor8Bit(this IGeometryAttributes self) => self.GetAttributeVertexColor8Bit()?.Data; - public static GeometryAttribute ToVertexBitangentAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexBitangent, index); - public static GeometryAttribute ToVertexBitangentAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexBitangent); - public static GeometryAttribute GetAttributeVertexBitangent(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexBitangent); - public static IList GetAttributeDataVertexBitangent(this IGeometryAttributes self) => self.GetAttributeVertexBitangent()?.Data; - public static GeometryAttribute ToVertexTangentAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexTangent, index); - public static GeometryAttribute ToVertexTangentAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexTangent); - public static GeometryAttribute GetAttributeVertexTangent(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexTangent); - public static IList GetAttributeDataVertexTangent(this IGeometryAttributes self) => self.GetAttributeVertexTangent()?.Data; - public static GeometryAttribute ToVertexSelectionWeightAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.VertexSelectionWeight, index); - public static GeometryAttribute ToVertexSelectionWeightAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.VertexSelectionWeight); - public static GeometryAttribute GetAttributeVertexSelectionWeight(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.VertexSelectionWeight); - public static IList GetAttributeDataVertexSelectionWeight(this IGeometryAttributes self) => self.GetAttributeVertexSelectionWeight()?.Data; - public static GeometryAttribute ToFaceColorAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.FaceColor, index); - public static GeometryAttribute ToFaceColorAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.FaceColor); - public static GeometryAttribute GetAttributeFaceColor(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.FaceColor); - public static IList GetAttributeDataFaceColor(this IGeometryAttributes self) => self.GetAttributeFaceColor()?.Data; - public static GeometryAttribute ToFaceMaterialAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.FaceMaterial, index); - public static GeometryAttribute ToFaceMaterialAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.FaceMaterial); - public static GeometryAttribute GetAttributeFaceMaterial(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.FaceMaterial); - public static IList GetAttributeDataFaceMaterial(this IGeometryAttributes self) => self.GetAttributeFaceMaterial()?.Data; - public static GeometryAttribute ToFaceNormalAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.FaceNormal, index); - public static GeometryAttribute ToFaceNormalAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.FaceNormal); - public static GeometryAttribute GetAttributeFaceNormal(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.FaceNormal); - public static IList GetAttributeDataFaceNormal(this IGeometryAttributes self) => self.GetAttributeFaceNormal()?.Data; - public static GeometryAttribute ToMeshSubmeshOffsetAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.MeshSubmeshOffset, index); - public static GeometryAttribute ToMeshSubmeshOffsetAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.MeshSubmeshOffset); - public static GeometryAttribute GetAttributeMeshSubmeshOffset(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.MeshSubmeshOffset); - public static IList GetAttributeDataMeshSubmeshOffset(this IGeometryAttributes self) => self.GetAttributeMeshSubmeshOffset()?.Data; - public static GeometryAttribute ToInstanceTransformAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.InstanceTransform, index); - public static GeometryAttribute ToInstanceTransformAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.InstanceTransform); - public static GeometryAttribute GetAttributeInstanceTransform(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.InstanceTransform); - public static IList GetAttributeDataInstanceTransform(this IGeometryAttributes self) => self.GetAttributeInstanceTransform()?.Data; - public static GeometryAttribute ToInstanceParentAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.InstanceParent, index); - public static GeometryAttribute ToInstanceParentAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.InstanceParent); - public static GeometryAttribute GetAttributeInstanceParent(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.InstanceParent); - public static IList GetAttributeDataInstanceParent(this IGeometryAttributes self) => self.GetAttributeInstanceParent()?.Data; - public static GeometryAttribute ToInstanceMeshAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.InstanceMesh, index); - public static GeometryAttribute ToInstanceMeshAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.InstanceMesh); - public static GeometryAttribute GetAttributeInstanceMesh(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.InstanceMesh); - public static IList GetAttributeDataInstanceMesh(this IGeometryAttributes self) => self.GetAttributeInstanceMesh()?.Data; - public static GeometryAttribute ToInstanceFlagsAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.InstanceFlags, index); - public static GeometryAttribute ToInstanceFlagsAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.InstanceFlags); - public static GeometryAttribute GetAttributeInstanceFlags(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.InstanceFlags); - public static IList GetAttributeDataInstanceFlags(this IGeometryAttributes self) => self.GetAttributeInstanceFlags()?.Data; - public static GeometryAttribute ToLineTangentInAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.LineTangentIn, index); - public static GeometryAttribute ToLineTangentInAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.LineTangentIn); - public static GeometryAttribute GetAttributeLineTangentIn(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.LineTangentIn); - public static IList GetAttributeDataLineTangentIn(this IGeometryAttributes self) => self.GetAttributeLineTangentIn()?.Data; - public static GeometryAttribute ToLineTangentOutAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.LineTangentOut, index); - public static GeometryAttribute ToLineTangentOutAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.LineTangentOut); - public static GeometryAttribute GetAttributeLineTangentOut(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.LineTangentOut); - public static IList GetAttributeDataLineTangentOut(this IGeometryAttributes self) => self.GetAttributeLineTangentOut()?.Data; - public static GeometryAttribute ToShapeVertexAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.ShapeVertex, index); - public static GeometryAttribute ToShapeVertexAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.ShapeVertex); - public static GeometryAttribute GetAttributeShapeVertex(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.ShapeVertex); - public static IList GetAttributeDataShapeVertex(this IGeometryAttributes self) => self.GetAttributeShapeVertex()?.Data; - public static GeometryAttribute ToShapeVertexOffsetAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.ShapeVertexOffset, index); - public static GeometryAttribute ToShapeVertexOffsetAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.ShapeVertexOffset); - public static GeometryAttribute GetAttributeShapeVertexOffset(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.ShapeVertexOffset); - public static IList GetAttributeDataShapeVertexOffset(this IGeometryAttributes self) => self.GetAttributeShapeVertexOffset()?.Data; - public static GeometryAttribute ToShapeColorAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.ShapeColor, index); - public static GeometryAttribute ToShapeColorAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.ShapeColor); - public static GeometryAttribute GetAttributeShapeColor(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.ShapeColor); - public static IList GetAttributeDataShapeColor(this IGeometryAttributes self) => self.GetAttributeShapeColor()?.Data; - public static GeometryAttribute ToShapeWidthAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.ShapeWidth, index); - public static GeometryAttribute ToShapeWidthAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.ShapeWidth); - public static GeometryAttribute GetAttributeShapeWidth(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.ShapeWidth); - public static IList GetAttributeDataShapeWidth(this IGeometryAttributes self) => self.GetAttributeShapeWidth()?.Data; - public static GeometryAttribute ToMaterialColorAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.MaterialColor, index); - public static GeometryAttribute ToMaterialColorAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.MaterialColor); - public static GeometryAttribute GetAttributeMaterialColor(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.MaterialColor); - public static IList GetAttributeDataMaterialColor(this IGeometryAttributes self) => self.GetAttributeMaterialColor()?.Data; - public static GeometryAttribute ToMaterialGlossinessAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.MaterialGlossiness, index); - public static GeometryAttribute ToMaterialGlossinessAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.MaterialGlossiness); - public static GeometryAttribute GetAttributeMaterialGlossiness(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.MaterialGlossiness); - public static IList GetAttributeDataMaterialGlossiness(this IGeometryAttributes self) => self.GetAttributeMaterialGlossiness()?.Data; - public static GeometryAttribute ToMaterialSmoothnessAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.MaterialSmoothness, index); - public static GeometryAttribute ToMaterialSmoothnessAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.MaterialSmoothness); - public static GeometryAttribute GetAttributeMaterialSmoothness(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.MaterialSmoothness); - public static IList GetAttributeDataMaterialSmoothness(this IGeometryAttributes self) => self.GetAttributeMaterialSmoothness()?.Data; - public static GeometryAttribute ToSubmeshIndexOffsetAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.SubmeshIndexOffset, index); - public static GeometryAttribute ToSubmeshIndexOffsetAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.SubmeshIndexOffset); - public static GeometryAttribute GetAttributeSubmeshIndexOffset(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.SubmeshIndexOffset); - public static IList GetAttributeDataSubmeshIndexOffset(this IGeometryAttributes self) => self.GetAttributeSubmeshIndexOffset()?.Data; - public static GeometryAttribute ToSubmeshMaterialAttribute(this IList xs, int index) => xs.ToAttribute(CommonAttributes.SubmeshMaterial, index); - public static GeometryAttribute ToSubmeshMaterialAttribute(this IList xs) => xs.ToAttribute(CommonAttributes.SubmeshMaterial); - public static GeometryAttribute GetAttributeSubmeshMaterial(this IGeometryAttributes self) => self.GetAttribute(CommonAttributes.SubmeshMaterial); - public static IList 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 e73884ef..00000000 --- a/src/cs/g3d/Vim.G3d/CommonAttributes.tt +++ /dev/null @@ -1,93 +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 System.Collections.Generic; -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 IList<<#= type #>> xs, int index) => xs.ToAttribute(<#= codeName #>, index); - public static GeometryAttribute<<#= type #>> To<#= name #>Attribute(this IList<<#= type #>> xs) => xs.ToAttribute(<#= codeName #>); - public static GeometryAttribute<<#= type #>> GetAttribute<#= name #>(this IGeometryAttributes self) => self.GetAttribute<<#= type #>>(<#= codeName #>); - public static IList<<#= 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 c4b97455..00000000 --- a/src/cs/g3d/Vim.G3d/G3D.cs +++ /dev/null @@ -1,345 +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.IO; -using System.Linq; -using Vim.Math3d; -using Vim.BFastLib; -using System.Diagnostics; - -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 IList Vertices { get; } - - // Index buffer (one index per corner, and per half-edge). Computed if absent. - public IList Indices { get; } - - // Vertex associated data, provided or null - public List> AllVertexUvs { get; } = new List>(); - public List> AllVertexColors { get; } = new List>(); - public IList VertexUvs => AllVertexUvs?.ElementAtOrDefault(0); - public IList VertexColors => AllVertexColors?.ElementAtOrDefault(0); - public IList VertexNormals { get; } - public IList VertexTangents { get; } - - // Faces - public IList FaceMaterials { get; } // Material indices per face, - public IList FaceNormals { get; } // If not provided, are computed dynamically as the average of all vertex normals, - - // Meshes - public IList MeshIndexOffsets { get; } // Offset into the index buffer for each Mesh - public IList MeshVertexOffsets { get; } // Offset into the vertex buffer for each Mesh - public IList MeshIndexCounts { get; } // Computed - public IList MeshVertexCounts { get; } // Computed - public IList MeshSubmeshOffset { get; } - public IList MeshSubmeshCount { get; } // Computed - public IList Meshes { get; } - - // Instances - public IList InstanceParents { get; } // Index of the parent transform - public IList InstanceTransforms { get; } // A 4x4 matrix in row-column order defining the transormed - public IList InstanceMeshes { get; } // The SubGeometry associated with the index - public IList InstanceFlags { get; } // The instance flags associated with the index. - - // Shapes - public IList ShapeVertices { get; } - public IList ShapeVertexOffsets { get; } - public IList ShapeColors { get; } - public IList ShapeWidths { get; } - public IList ShapeVertexCounts { get; } // Computed - public IList Shapes { get; } // Computed - - // Materials - public IList MaterialColors { get; } // RGBA with transparency. - public IList MaterialGlossiness { get; } - public IList MaterialSmoothness { get; } - public IList Materials { get; } - - - // Submeshes - public IList SubmeshIndexOffsets { get; } - public IList SubmeshIndexCount { get; } - public IList SubmeshMaterials { get; } - - public G3D(IEnumerable attributes, G3dHeader? header = null, int numCornersPerFaceOverride = -1) - : base(attributes, numCornersPerFaceOverride) - { - Header = header ?? new G3dHeader(); - - foreach (var attr in Attributes) - { - 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).ToArray(); - 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()).ToArray(); - 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()).ToArray()); - 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().ToArray(); - - // 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]).ToArray(); - MeshSubmeshCount = GetSubArrayCounts(MeshSubmeshOffset.Count, MeshSubmeshOffset, NumSubmeshes); - } - - 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()) - .ToArray(); - } - - if (MeshVertexOffsets != null) - MeshVertexCounts = GetSubArrayCounts(NumMeshes, MeshVertexOffsets, NumVertices); - } - else - { - MeshSubmeshCount = Array.Empty(); - } - - if (SubmeshIndexOffsets != null) - SubmeshIndexCount = GetSubArrayCounts(SubmeshIndexOffsets.Count, SubmeshIndexOffsets, NumCorners); - - // 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(); - - if (ShapeColors == null) - ShapeColors = Vector4.Zero.Repeat(0); - - if (ShapeWidths == null) - ShapeWidths = Array.Empty(); - - // 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 IList GetSubArrayCounts(int numItems, IList offsets, int totalCount) - => numItems.Select(i => i < (numItems - 1) - ? offsets[i + 1] - offsets[i] - : totalCount - offsets[i]); - - private static void ValidateSubArrayCounts(IList 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."); - } - } - - private static Vector3 Average(IList xs) - => xs.Aggregate(Vector3.Zero, (a, b) => a + b) / xs.Count; - - private Vector3 ComputeFaceNormal(int nFace) - => Average(NumCornersPerFace.Select(c => VertexNormals[nFace * NumCornersPerFace + c])); - - public static G3D Read(string filePath) - { - using (var stream = File.OpenRead(filePath)) - { - var bfast = new BFast(stream); - return Read(bfast); - } - } - - - public static G3D Read(BFast bfast) - { - var header = G3dHeader.FromBytesOrDefault(bfast.GetArray("meta")); - var attributes = new List(); - foreach (var name in bfast.Entries) - { - if (name == "meta") continue; - var attribute = GetEmptyAttribute(name); - if (attribute == null) continue; - var a = attribute.Read(bfast); - attributes.Add(a); - } - - return new G3D(attributes, header); - } - private static GeometryAttribute GetEmptyAttribute(string name) - { - if (!AttributeDescriptor.TryParse(name, out var attributeDescriptor)) - { - Debug.WriteLine("G3D Error: Could not parse attribute " + name); - return null; - } - try - { - return attributeDescriptor.ToDefaultAttribute(0); - } - catch - { - Debug.WriteLine("G3D Error: Could not parse attribute " + name); - return null; - } - } - - - 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 28c0a63a..00000000 --- a/src/cs/g3d/Vim.G3d/G3DBuilder.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; -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(IList indices) - => Add(indices.ToIndexAttribute()); - - public G3DBuilder SetObjectFaceSize(int objectFaceSize) - => Add(new[] { objectFaceSize }.ToObjectFaceSizeAttribute()); - - public G3DBuilder AddVertices(IList vertices) - => Add(vertices.ToPositionAttribute()); - - public IGeometryAttributes ToIGeometryAttributes() - => new GeometryAttributes(Attributes); - } -} - diff --git a/src/cs/g3d/Vim.G3dNext/G3dChunk.cs b/src/cs/g3d/Vim.G3d/G3dChunk.cs similarity index 99% rename from src/cs/g3d/Vim.G3dNext/G3dChunk.cs rename to src/cs/g3d/Vim.G3d/G3dChunk.cs index 8ad82bd3..c85d9bc9 100644 --- a/src/cs/g3d/Vim.G3dNext/G3dChunk.cs +++ b/src/cs/g3d/Vim.G3d/G3dChunk.cs @@ -1,8 +1,11 @@ using System; +using System.Linq; using Vim.Math3d; -namespace Vim.G3dNext +namespace Vim.G3d { + + public partial class G3dChunk { void ISetup.Setup() @@ -10,6 +13,7 @@ void ISetup.Setup() // empty } + public int GetSubmeshCount() => SubmeshIndexOffsets?.Length ?? 0; public int getMeshCount() => MeshSubmeshOffset?.Length ?? 0; diff --git a/src/cs/g3d/Vim.G3dNext/G3dGenerated.g.cs b/src/cs/g3d/Vim.G3d/G3dGenerated.g.cs similarity index 83% rename from src/cs/g3d/Vim.G3dNext/G3dGenerated.g.cs rename to src/cs/g3d/Vim.G3d/G3dGenerated.g.cs index 5d2a7b8d..21c085f8 100644 --- a/src/cs/g3d/Vim.G3dNext/G3dGenerated.g.cs +++ b/src/cs/g3d/Vim.G3d/G3dGenerated.g.cs @@ -2,7 +2,7 @@ // ReSharper disable All using Vim.BFastLib; -namespace Vim.G3dNext +namespace Vim.G3d { // Please provide an explicit implementation in another partial class file. public partial class G3dVim : ISetup @@ -109,6 +109,11 @@ public BFast ToBFast() return bfast; } + public G3dVim Clone() + { + return this.MemberwiseClone() as G3dVim; + } + public bool Equals(G3dVim other ) { return BufferMethods.SafeEquals(Indices, other.Indices) && @@ -151,6 +156,27 @@ public G3dVim Merge(G3dVim other) ); } + 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. @@ -224,6 +250,11 @@ public BFast ToBFast() return bfast; } + public G3dChunk Clone() + { + return this.MemberwiseClone() as G3dChunk; + } + public bool Equals(G3dChunk other ) { return BufferMethods.SafeEquals(MeshOpaqueSubmeshCounts, other.MeshOpaqueSubmeshCounts) && @@ -248,6 +279,18 @@ public G3dChunk Merge(G3dChunk other) ); } + 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. @@ -298,6 +341,11 @@ public BFast ToBFast() return bfast; } + public G3dMaterials Clone() + { + return this.MemberwiseClone() as G3dMaterials; + } + public bool Equals(G3dMaterials other ) { return BufferMethods.SafeEquals(MaterialColors, other.MaterialColors) && @@ -314,6 +362,14 @@ public G3dMaterials Merge(G3dMaterials other) ); } + 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. @@ -421,6 +477,11 @@ public BFast ToBFast() return bfast; } + public G3dScene Clone() + { + return this.MemberwiseClone() as G3dScene; + } + public bool Equals(G3dScene other ) { return BufferMethods.SafeEquals(ChunkCount, other.ChunkCount) && @@ -461,6 +522,26 @@ public G3dScene Merge(G3dScene other) ); } + 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.G3dNext/G3dMaterials.cs b/src/cs/g3d/Vim.G3d/G3dMaterials.cs similarity index 94% rename from src/cs/g3d/Vim.G3dNext/G3dMaterials.cs rename to src/cs/g3d/Vim.G3d/G3dMaterials.cs index 18c3138a..28888a7e 100644 --- a/src/cs/g3d/Vim.G3dNext/G3dMaterials.cs +++ b/src/cs/g3d/Vim.G3d/G3dMaterials.cs @@ -1,5 +1,5 @@  -namespace Vim.G3dNext +namespace Vim.G3d { public partial class G3dMaterials { diff --git a/src/cs/g3d/Vim.G3d/G3dMesh.cs b/src/cs/g3d/Vim.G3d/G3dMesh.cs deleted file mode 100644 index 69ee18e4..00000000 --- a/src/cs/g3d/Vim.G3d/G3dMesh.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -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).ToArray(); - 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; - var submeshIndex = Array.BinarySearch(submeshArray.ToArray(), IndexOffset); - var submeshCount = 0; - for(var i = submeshIndex; i < submeshArray.Count; 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).ToArray(); - MeshSubmeshOffset = Array.Empty(); - } - - // Vertex buffer. Usually present. - public IList Vertices { get; } - - // Index buffer (one index per corner, and per half-edge) - public IList Indices { get; } - - // Vertex associated data - public IList VertexUvs { get; } - public IList VertexNormals { get; } - public IList VertexColors { get; } - public IList VertexTangents { get; } - - // Face associated data. - public IList FaceNormals { get; } - - public IList SubmeshMaterials { get; } - public IList SubmeshIndexOffsets { get; } - public IList MeshSubmeshOffset { get; } - } -} diff --git a/src/cs/g3d/Vim.G3dNext/G3dScene.cs b/src/cs/g3d/Vim.G3d/G3dScene.cs similarity index 90% rename from src/cs/g3d/Vim.G3dNext/G3dScene.cs rename to src/cs/g3d/Vim.G3d/G3dScene.cs index 4d6adb03..1fe8d9e2 100644 --- a/src/cs/g3d/Vim.G3dNext/G3dScene.cs +++ b/src/cs/g3d/Vim.G3d/G3dScene.cs @@ -1,4 +1,4 @@ -namespace Vim.G3dNext +namespace Vim.G3d { public partial class G3dScene { diff --git a/src/cs/g3d/Vim.G3d/G3dSerialization.cs b/src/cs/g3d/Vim.G3d/G3dSerialization.cs deleted file mode 100644 index 7a17fc04..00000000 --- a/src/cs/g3d/Vim.G3d/G3dSerialization.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.IO; -using Vim.BFastLib; - -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 BFast ToBFast(this IGeometryAttributes self, G3dHeader? header = null) - { - var bfast = new BFast(); - bfast.SetArray("meta", (header ?? G3dHeader.Default).ToBytes()); - foreach(var attribute in self.Attributes) - { - attribute.AddTo(bfast); - } - return bfast; - } - - - - } -} diff --git a/src/cs/g3d/Vim.G3d/G3dShape.cs b/src/cs/g3d/Vim.G3d/G3dShape.cs deleted file mode 100644 index d78a7faf..00000000 --- a/src/cs/g3d/Vim.G3d/G3dShape.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; -using Vim.Math3d; - -namespace Vim.G3d -{ - public class G3dShape - { - public readonly G3D G3D; - public readonly int Index; - public readonly IList 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.G3dNext/G3dVim.cs b/src/cs/g3d/Vim.G3d/G3dVim.cs similarity index 79% rename from src/cs/g3d/Vim.G3dNext/G3dVim.cs rename to src/cs/g3d/Vim.G3d/G3dVim.cs index 3ddb9885..78ab447b 100644 --- a/src/cs/g3d/Vim.G3dNext/G3dVim.cs +++ b/src/cs/g3d/Vim.G3d/G3dVim.cs @@ -2,10 +2,26 @@ using System.Collections.Generic; using Vim.BFastLib; -namespace Vim.G3dNext +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; @@ -20,6 +36,11 @@ 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(); @@ -66,9 +87,14 @@ private List[] ComputeMeshInstances() return result; } - public int GetTriangleCount() + public G3dVim RemoveShapes() { - return GetIndexCount() / 3; + var result = Clone(); + result.ShapeVertexOffsets = null; + result.ShapeWidths = null; + result.ShapeColors = null; + result.ShapeVertices = null; + return result; } /// @@ -177,9 +203,25 @@ public int GetSubmeshIndexCount(int submesh) /// 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() => ShapeVertices?.Length ?? 0; + public int GetShapeVertexCount(int index) + { + return GetShapeVertexEnd(index) - GetShapeVertexStart(index); + } } } diff --git a/src/cs/g3d/Vim.G3d/GeometryAttribute.cs b/src/cs/g3d/Vim.G3d/GeometryAttribute.cs deleted file mode 100644 index 7a09be78..00000000 --- a/src/cs/g3d/Vim.G3d/GeometryAttribute.cs +++ /dev/null @@ -1,243 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Vim.Math3d; -using Vim.BFastLib; -using Vim.BFastLib.Core; - -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(IList 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); - - /// - /// Loads the correct typed data from a BFastNext. - /// - public abstract GeometryAttribute Read(BFast bfast); - - public abstract void AddTo(BFast bfast); - - /// - /// 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 IList Data; - - public GeometryAttribute(IList 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) - .SelectMany(attr => attr.Data) - .ToArray() - .ToAttribute(Descriptor); - } - - public override GeometryAttribute Remap(IList 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, Descriptor); - } - - public override GeometryAttribute Read(BFast bfast) - { - var array = bfast.GetArray(Name); - return new GeometryAttribute(array, Descriptor); - } - - public override void AddTo(BFast bfast) - { - bfast.SetArray(Name, Data.ToArray()); - } - - 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 fd83c81d..00000000 --- a/src/cs/g3d/Vim.G3d/GeometryAttributes.cs +++ /dev/null @@ -1,163 +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; - -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 IList 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).ToArray(); - - - // 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 d7c5d85c..00000000 --- a/src/cs/g3d/Vim.G3d/GeometryAttributesExtensions.cs +++ /dev/null @@ -1,510 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -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 string[] AttributeNames(this IGeometryAttributes g) - => g.Attributes.Select(attr => attr.Name).ToArray(); - - 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 IList FaceIndicesToCornerIndices(this IGeometryAttributes g, IList 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 int[] FaceIndicesToFirstCornerIndices(this IGeometryAttributes g, IList faceIndices) - => faceIndices.Select(f => f * g.NumCornersPerFace).ToArray(); - - public static int CornerToFace(this IGeometryAttributes g, int c) - => c / g.NumCornersPerFace; - - public static IList 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 AddAttributes(this IGeometryAttributes attributes, params GeometryAttribute[] newAttributes) - => attributes.Attributes.Concat(newAttributes).ToGeometryAttributes(); - - public static GeometryAttribute GetAttributeOrDefault(this IGeometryAttributes g, string name) - => g.GetAttribute(name) ?? g.DefaultAttribute(name); - - public static IGeometryAttributes Merge(this IGeometryAttributes self, params IGeometryAttributes[] gs) - => gs.Prepend(self).Merge(); - - public static IGeometryAttributes Merge(this IList 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); - 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 IList 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 IList 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).ToArray().ToAttribute(a.Descriptor) : - (a.Descriptor.Semantic == Semantic.Normal && a is GeometryAttribute n) ? n.Data.Select(normalTransform).ToArray().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).ToArray().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, Vector3[] 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, ValueT[] 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, IList 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, IList faceRemap, IList 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, cornerRemap, 3); - } - - public static IGeometryAttributes CopyFaces(this IGeometryAttributes g, bool[] keep) - => g.CopyFaces(i => keep[i]); - - public static IGeometryAttributes CopyFaces(this IGeometryAttributes g, int[] keep) - => g.RemapFaces(keep); - - public static IGeometryAttributes CopyFaces(this IGeometryAttributes self, Func predicate) - => self.RemapFaces(self.NumFaces.Select(i => i).Indices().Where(predicate).ToArray()); - - 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, int[] newVertices, int[] 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, int[] vertRemap) - { - var vertLookup = (-1).Repeat(g.NumVertices).ToArray(); - for (var i = 0; i < vertRemap.Length; ++i) - { - var oldVert = vertRemap[i]; - vertLookup[oldVert] = i; - } - - var oldIndices = g.GetAttributeIndex()?.Data ?? g.NumVertices.Range(); - var newIndices = oldIndices.Select(i => vertLookup[i]).ToArray(); - - 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, int[] faces) - { - // Early exit, if all selected no need to do anything - if (g.NumFaces == faces.Length) - return g; - - // Early exit, if none selected no need to do anything - if (faces.Length == 0) - return null; - - // First, get all the indices for this array of faces - var oldIndices = g.GetAttributeIndex(); - var oldSelIndices = new int[faces.Length * g.NumCornersPerFace]; - // var oldIndices = faces.SelectMany(f => f.Indices()); - for (var i = 0; i < faces.Length; 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).ToArray(); - 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 IList 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()).ToArray().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 IList DefaultMaterials(this IGeometryAttributes self) - => (-1).Repeat(self.NumFaces); - - public static IList DefaultColors(this IGeometryAttributes self) - => Vector4.Zero.Repeat(self.NumVertices); - - public static IList 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 60d4bb09..00000000 --- a/src/cs/g3d/Vim.G3d/Header.cs +++ /dev/null @@ -1,81 +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 FromBytesOrDefault(byte[] bytes) - { - try - { - return FromBytes(bytes).Validate(); - } - catch (Exception) - { - return Default; - } - } - - 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 b83abf02..00000000 --- a/src/cs/g3d/Vim.G3d/IGeometryAttributes.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; - -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; } - - IList Attributes { get; } - GeometryAttribute GetAttribute(string name); - } -} diff --git a/src/cs/g3d/Vim.G3dNext/MetaHeader.cs b/src/cs/g3d/Vim.G3d/MetaHeader.cs similarity index 99% rename from src/cs/g3d/Vim.G3dNext/MetaHeader.cs rename to src/cs/g3d/Vim.G3d/MetaHeader.cs index a58a3a6f..b79db7ea 100644 --- a/src/cs/g3d/Vim.G3dNext/MetaHeader.cs +++ b/src/cs/g3d/Vim.G3d/MetaHeader.cs @@ -1,7 +1,7 @@ using System; using System.Text; -namespace Vim.G3dNext +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 diff --git a/src/cs/g3d/Vim.G3d/ObjExporter.cs b/src/cs/g3d/Vim.G3d/ObjExporter.cs deleted file mode 100644 index bd01456c..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) - 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 1d7bfd8c..00000000 --- a/src/cs/g3d/Vim.G3d/Validation.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -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 f3bb78c2..b0086596 100644 --- a/src/cs/g3d/Vim.G3d/Vim.G3d.csproj +++ b/src/cs/g3d/Vim.G3d/Vim.G3d.csproj @@ -1,57 +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/g3d/Vim.G3dNext.Tests/Vim.G3dNext.Tests.csproj b/src/cs/g3d/Vim.G3dNext.Tests/Vim.G3dNext.Tests.csproj deleted file mode 100644 index 158c815b..00000000 --- a/src/cs/g3d/Vim.G3dNext.Tests/Vim.G3dNext.Tests.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - netstandard2.0 - false - - - - - - - - - - - - - - - - - True - True - Resources.resx - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - - - - diff --git a/src/cs/g3d/Vim.G3dNext/Constants.cs b/src/cs/g3d/Vim.G3dNext/Constants.cs deleted file mode 100644 index c8cc2282..00000000 --- a/src/cs/g3d/Vim.G3dNext/Constants.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace Vim.G3dNext -{ - /// - /// 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 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 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/samples/Vim.Gltf.Converter/VimToGltfStore.cs b/src/cs/samples/Vim.Gltf.Converter/VimToGltfStore.cs index b4995bc2..0df07ce4 100644 --- a/src/cs/samples/Vim.Gltf.Converter/VimToGltfStore.cs +++ b/src/cs/samples/Vim.Gltf.Converter/VimToGltfStore.cs @@ -4,7 +4,10 @@ using System.Linq; using System.Numerics; using Vim.BFastLib; +using Vim.Format.Geometry; using Vim.G3d; +using Vim.Util; +using static Vim.Format.DocumentBuilder; namespace Vim.Gltf.Converter { @@ -46,22 +49,23 @@ public static void Convert(string vimFilePath, string gltfFilePath, float scale } } - var g3d = vim._SerializableDocument.Geometry; + var g3d = vim._SerializableDocument.GeometryNext; + var mesh = VimMesh.FromG3d(g3d); // Initialize a flat vertex buffer. - var vimVertexBufferBytes = g3d.Vertices.ToArray().SelectMany(v => new [] { v.X, v.Y, v.Z }).ToArray().ToBytes(); + var vimVertexBufferBytes = mesh.vertices.ToArray().SelectMany(v => new [] { v.X, v.Y, v.Z }).ToArray().ToBytes(); var gltfVertexBufferView = gltfModel.UseBufferView(vimVertexBufferBytes); var gltfVertexAccessor = gltfModel.CreateAccessor("VERTEX_ACCESSOR"); - gltfVertexAccessor.SetVertexData(gltfVertexBufferView, 0, g3d.Vertices.Count); + gltfVertexAccessor.SetVertexData(gltfVertexBufferView, 0, mesh.vertices.Length); // Initialize a flat index buffer. - var vimIndexBufferBytes = g3d.Indices.Select(i => (uint)i).ToArray().ToBytes(); + var vimIndexBufferBytes = mesh.indices.Select(i => (uint)i).ToArray().ToBytes(); var gltfIndexBufferView = gltfModel.UseBufferView(vimIndexBufferBytes); // Create the meshes and their primitives (submeshes) - var meshSubmeshCounts = g3d.MeshSubmeshCount.ToArray(); - var meshSubmeshOffsets = g3d.MeshSubmeshOffset.ToArray(); - for (var meshIndex = 0; meshIndex < meshSubmeshCounts.Length; ++meshIndex) + var meshSubmeshCounts = mesh.SubmeshCount.Select(m => g3d.GetMeshSubmeshCount(m)); + var meshSubmeshOffsets = mesh.SubmeshCount.Select(m => g3d.GetMeshSubmeshStart(m)); + for (var meshIndex = 0; meshIndex < meshSubmeshCounts.Count; ++meshIndex) { var meshSubmeshCount = meshSubmeshCounts[meshIndex]; if (meshSubmeshCount <= 0) @@ -73,9 +77,9 @@ public static void Convert(string vimFilePath, string gltfFilePath, float scale for (var submeshIndex = meshSubmeshOffset; submeshIndex < meshSubmeshOffset + meshSubmeshCount; ++submeshIndex) { - var submeshIndexOffset = g3d.SubmeshIndexOffsets[submeshIndex]; - var submeshIndexCount = g3d.SubmeshIndexCount[submeshIndex]; - var submeshMaterialIndex = g3d.SubmeshMaterials[submeshIndex]; + var submeshIndexOffset = mesh.submeshIndexOffsets[submeshIndex]; + var submeshIndexCount = mesh.submeshIndexCounts[submeshIndex]; + var submeshMaterialIndex = mesh.submeshMaterials[submeshIndex]; var gltfPrimitive = gltfMesh.CreatePrimitive(); gltfPrimitive.SetVertexAccessor("POSITION", gltfVertexAccessor); diff --git a/src/cs/samples/Vim.JsonDigest/MaterialInfo.cs b/src/cs/samples/Vim.JsonDigest/MaterialInfo.cs index f4efdf86..9ad7e26f 100644 --- a/src/cs/samples/Vim.JsonDigest/MaterialInfo.cs +++ b/src/cs/samples/Vim.JsonDigest/MaterialInfo.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using Vim.LinqArray; namespace Vim.JsonDigest { diff --git a/src/cs/samples/Vim.JsonDigest/RoomInfo.cs b/src/cs/samples/Vim.JsonDigest/RoomInfo.cs index 6a9d6ddc..2759c608 100644 --- a/src/cs/samples/Vim.JsonDigest/RoomInfo.cs +++ b/src/cs/samples/Vim.JsonDigest/RoomInfo.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using Vim.LinqArray; namespace Vim.JsonDigest { diff --git a/src/cs/g3d/Vim.G3d/ArrayExtensions.cs b/src/cs/util/Vim.Util/ArrayExtensions.cs similarity index 89% rename from src/cs/g3d/Vim.G3d/ArrayExtensions.cs rename to src/cs/util/Vim.Util/ArrayExtensions.cs index 3ac12173..2adf4a2f 100644 --- a/src/cs/g3d/Vim.G3d/ArrayExtensions.cs +++ b/src/cs/util/Vim.Util/ArrayExtensions.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace Vim.G3d +namespace Vim.Util { public static class ArrayExtensions { @@ -52,9 +52,6 @@ public static IList Range(this int self) public static IList Prepend(this IList self, T x) => (self.Count + 1).Select(i => i == 0 ? x : self[i - 1]); - public static T ElementAtOrDefault(this IList xs, int n, T defaultValue = default) - => xs != null && n >= 0 && n < xs.Count ? xs[n] : defaultValue; - /// /// 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. @@ -77,5 +74,18 @@ public static IList PostAccumulate(this IList self, Func f, T /// public static IList DropLast(this IList self, int n = 1) => self.Count > n ? self.Take(self.Count - n).ToArray() : Array.Empty(); + + public static int IndexOf(this long[] self, long value) + { + for (var i = 0; i < self.Length; i++) + { + if (self[i] == value) + { + return i; + } + } + + return -1; + } } } diff --git a/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs b/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs index 70c4529a..226f6bfe 100644 --- a/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs +++ b/src/cs/vim/Vim.Format.CodeGen/ObjectModelGenerator.cs @@ -92,7 +92,7 @@ private static CodeBuilder WriteDocumentEntityData(Type t, CodeBuilder cb, Entit ? $"({string.Join(" ?? ", dataColumnGetters)})" : dataColumnGetters[0]; - cb.AppendLine($"public IList<{fieldTypeName}> {t.Name}{fieldName} {{ get; }}"); + cb.AppendLine($"public {fieldTypeName}[] {t.Name}{fieldName} {{ get; }}"); constructor.ArraysInitializers .Add($"{t.Name}{fieldName} = {dataColumnGetterString} ?? Array.Empty<{fieldTypeName}>();"); @@ -106,7 +106,7 @@ private static CodeBuilder WriteDocumentEntityData(Type t, CodeBuilder cb, Entit { var (indexColumnName, localFieldName) = fieldInfo.GetIndexColumnInfo(); - cb.AppendLine($"public IList {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();"); @@ -117,7 +117,7 @@ private static CodeBuilder WriteDocumentEntityData(Type t, CodeBuilder cb, Entit cb.AppendLine($"public int Num{t.Name} => {t.Name}EntityTable?.NumRows ?? 0;"); // Entity lists - cb.AppendLine($"public IList<{t.Name}> {t.Name}List {{ get; }}"); + cb.AppendLine($"public {t.Name}[] {t.Name}List {{ get; }}"); // Element getter function cb.AppendLine($"public {t.Name} Get{t.Name}(int n)"); @@ -222,8 +222,10 @@ IEnumerable GetEquatableFields(FieldInfo[] fis) return cb; } - private static CodeBuilder WriteDocument(CodeBuilder cb) + private static CodeBuilder WriteDocument(CodeBuilder cb = null) { + cb = cb ?? new CodeBuilder(); + var entityTypes = ObjectModelReflection.GetEntityTypes().ToArray(); foreach (var et in entityTypes) @@ -275,7 +277,7 @@ private static CodeBuilder WriteDocument(CodeBuilder cb) cb.AppendLine("// Initialize entity collections"); foreach (var t in entityTypes) - cb.AppendLine($"{t.Name}List = Num{t.Name}.Select(i => Get{t.Name}(i));"); + cb.AppendLine($"{t.Name}List = Enumerable.Range(0, Num{t.Name}).Select(i => Get{t.Name}(i)).ToArray();"); cb.AppendLine(); cb.AppendLine("// Initialize element index maps"); @@ -502,11 +504,11 @@ public static void WriteDocument(string file) cb.AppendLine("// AUTO-GENERATED FILE, DO NOT MODIFY."); cb.AppendLine("// ReSharper disable All"); cb.AppendLine("using System;"); - cb.AppendLine("using System.Collections;"); cb.AppendLine("using System.Collections.Generic;"); cb.AppendLine("using System.Linq;"); cb.AppendLine("using Vim.Math3d;"); - cb.AppendLine("using Vim.G3d;"); + 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 f5312e23..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.BFastLib; using Vim.Util; -using Vim.LinqArray; +using Vim.BFastLib; +using System.Linq; namespace Vim.Format { @@ -18,16 +18,16 @@ public enum AssetType /// public class AssetInfo { - private const char Separator = '/'; + public const char Separator = '/'; - public string Name { get; private set; } + public readonly string Name; - private readonly AssetType _assetType; + public readonly AssetType AssetType; public AssetInfo(string name, AssetType assetType) - => (Name, _assetType) = (name, assetType); + => (Name, AssetType) = (name, assetType); - private static string[] SplitAssetBufferName(string assetBufferName) + public static string[] SplitAssetBufferName(string assetBufferName) => assetBufferName?.Split(Separator); public static AssetInfo Parse(string assetBufferName) @@ -63,11 +63,11 @@ public static bool TryParse(string assetBufferName, out AssetInfo assetInfo) } } - private static string AssetTypeToString(AssetType assetType) + public static string AssetTypeToString(AssetType assetType) => assetType.ToString("G").ToLowerInvariant(); - private string AssetTypeString - => AssetTypeToString(_assetType); + public string AssetTypeString + => AssetTypeToString(AssetType); public override string ToString() => $"{AssetTypeString}{Separator}{Name}"; @@ -81,13 +81,19 @@ public string GetDefaultAssetFilePathInDirectory(DirectoryInfo directoryInfo) public static class AssetInfoExtensions { - private static INamedBuffer GetAssetBuffer(this Document doc, string assetBufferName) + public static bool IsTexture(this INamedBuffer buffer) + => AssetInfo.TryParse(buffer.Name, out var assetInfo) && assetInfo.AssetType == AssetType.Texture; + + public static INamedBuffer GetAssetBuffer(this Document doc, string assetBufferName) => doc.Assets.GetOrDefault(assetBufferName); + public static IEnumerable GetTextures(this Document d) + => d.Assets.Values.Where(IsTexture); + /// /// Extracts the asset buffer to the file designated by the given FileInfo. /// - private static FileInfo ExtractAsset(this INamedBuffer assetBuffer, FileInfo fileInfo) + public static FileInfo ExtractAsset(this INamedBuffer assetBuffer, FileInfo fileInfo) { Util.IO.CreateFileDirectory(fileInfo.FullName); using (var stream = fileInfo.Create()) @@ -99,11 +105,18 @@ private static FileInfo ExtractAsset(this INamedBuffer assetBuffer, FileInfo fil /// Extracts the asset and returns a FileInfo representing the extracted asset on disk.
/// Returns null if the asset could not be extracted. ///
- private static FileInfo ExtractAsset(this INamedBuffer assetBuffer, DirectoryInfo directoryInfo) + public static FileInfo ExtractAsset(this INamedBuffer assetBuffer, DirectoryInfo directoryInfo) => !AssetInfo.TryParse(assetBuffer.Name, out var assetInfo) ? null : assetBuffer.ExtractAsset(new FileInfo(assetInfo.GetDefaultAssetFilePathInDirectory(directoryInfo))); + /// + /// Extracts the asset corresponding to the assetBufferName and returns a FileInfo representing the extracted asset on disk.
+ /// Returns null if the asset could not be extracted. + ///
+ public static FileInfo ExtractAsset(this Document doc, string assetBufferName, FileInfo fileInfo) + => doc.GetAssetBuffer(assetBufferName)?.ExtractAsset(fileInfo); + /// /// Extracts the assets contained in the Document to the given directory. /// @@ -122,7 +135,7 @@ private static FileInfo ExtractAsset(this INamedBuffer assetBuffer, DirectoryInf /// /// Gets the byte array which defines the given asset. Returns false if the asset was not found or if the byte array is empty or null. /// - private static bool TryGetAssetBytes(this Document doc, string assetBufferName, out byte[] bytes) + public static bool TryGetAssetBytes(this Document doc, string assetBufferName, out byte[] bytes) { bytes = null; @@ -138,7 +151,7 @@ private static bool TryGetAssetBytes(this Document doc, string assetBufferName, /// /// Gets the byte array which defines the given asset. Returns false if the asset was not found or if the byte array is empty or null. /// - private static bool TryGetAssetBytes(this Document doc, AssetType assetType, string assetName, out byte[] bytes) + public static bool TryGetAssetBytes(this Document doc, AssetType assetType, string assetName, out byte[] bytes) => doc.TryGetAssetBytes(new AssetInfo(assetName, assetType).ToString(), out bytes); /// diff --git a/src/cs/vim/Vim.Format.Core/BigG3dWriter.cs b/src/cs/vim/Vim.Format.Core/BigG3dWriter.cs deleted file mode 100644 index 61cb63e8..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.G3dNext; -//using Vim.BFastLib; -//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 -// { -// private INamedBuffer Meta { get; } -// private string[] Names { get; } -// private long[] Sizes { get; } -// private BFastHeader Header { get; } -// private List Meshes { get; } -// private List Instances { get; } -// private List Shapes { get; } -// private List Materials { get; } - -// // Computed fields -// private int[] MeshVertexOffsets { get; } -// private int[] MeshIndexOffsets { get; } -// private int[] MeshSubmeshOffset { get; } -// private int[] SubmeshIndexOffsets { get; } -// private 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 7de080a0..5fcde0f2 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Buffer.cs @@ -3,21 +3,17 @@ using System.Diagnostics; using System.Linq; using Vim.BFastLib; -using Vim.G3d; +using Vim.Util; namespace Vim.Format { public static partial class ColumnExtensions { - private static IEnumerable GetAllColumns(this SerializableEntityTable et) - => et.DataColumns.Concat(et.IndexColumns).Concat(et.StringColumns); - - public static IList ValidateColumnRowsAreAligned(this IEnumerable columns) + public static void ValidateColumnRowsAreAligned(this IEnumerable columns) { - var result = columns.ToArray(); - var numRows = result.FirstOrDefault()?.NumElements() ?? 0; + var numRows = columns.FirstOrDefault()?.NumElements() ?? 0; - foreach (var column in result) + foreach (var column in columns) { var columnRows = column.NumElements(); if (columnRows == numRows) @@ -26,20 +22,21 @@ public static IList ValidateColumnRowsAreAligned(this IEnumerable< var msg = $"Column '{column.Name}' has {columnRows} rows which does not match the first column's {numRows} rows"; Debug.Fail(msg); } - - return result; } - public static IList ValidateColumnRowsAreAligned(this SerializableEntityTable et) - => et.GetAllColumns().ValidateColumnRowsAreAligned(); + public static SerializableEntityTable ValidateColumnRowsAreAligned(this SerializableEntityTable et) + { + et.AllColumns.ValidateColumnRowsAreAligned(); + return et; + } - private static string ValidateCanConcatBuffers(this INamedBuffer thisBuffer, INamedBuffer otherBuffer) + 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"); @@ -55,9 +52,9 @@ public static T[] RemapData(this T[] self, List remapping = null) public static IBuffer ToBuffer(this T[] array) where T : unmanaged => new Buffer(array); - private const string UnknownNamedBufferPrefix = "Unknown NamedBuffer prefix"; + public const string UnknownNamedBufferPrefix = "Unknown NamedBuffer prefix"; - private static object GetDataColumnValue(this IBuffer dataColumn, string typePrefix, int rowIndex) + public static object GetDataColumnValue(this IBuffer dataColumn, string typePrefix, int rowIndex) { switch (typePrefix) { @@ -77,7 +74,10 @@ private static object GetDataColumnValue(this IBuffer dataColumn, string typePre } 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,11 +119,11 @@ public static IBuffer CopyDataColumn(this IBuffer dataColumn, string typePrefix, public static INamedBuffer CopyDataColumn(this INamedBuffer dataColumn, List remapping = null) { - var typePrefix = dataColumn.GetTypePrefix(); + var typePrefix = SerializableEntityTable.GetTypeFromName(dataColumn.Name); return new NamedBuffer(dataColumn.CopyDataColumn(typePrefix, remapping), dataColumn.Name); } - private static IBuffer Concat(this IBuffer thisBuffer, IBuffer otherBuffer) where T : unmanaged + public static IBuffer Concat(this IBuffer thisBuffer, IBuffer otherBuffer) where T : unmanaged => thisBuffer.AsArray().Concat(otherBuffer.AsArray()).ToArray().ToBuffer(); public static IBuffer ConcatDataColumnBuffers(this IBuffer thisBuffer, IBuffer otherBuffer, string typePrefix) @@ -145,14 +145,14 @@ public static IBuffer ConcatDataColumnBuffers(this IBuffer thisBuffer, IBuffer o } } - private static INamedBuffer ConcatDataColumns(this INamedBuffer thisColumn, INamedBuffer otherColumn) + public static INamedBuffer ConcatDataColumns(this INamedBuffer thisColumn, INamedBuffer otherColumn) { var typePrefix = thisColumn.ValidateCanConcatBuffers(otherColumn); var combinedBuffer = thisColumn.ConcatDataColumnBuffers(otherColumn, typePrefix); return new NamedBuffer(combinedBuffer, thisColumn.Name); } - private static List ConcatColumns( + public static List ConcatColumns( this IReadOnlyList thisColumnList, IReadOnlyList otherColumnList, Func concatFunc) where T : INamedBuffer @@ -173,11 +173,11 @@ private static List ConcatColumns( return mergedColumns; } - private static List ConcatDataColumns(this IReadOnlyList thisColumnList, IReadOnlyList otherColumnList) + public static List ConcatDataColumns(this IReadOnlyList thisColumnList, IReadOnlyList otherColumnList) => thisColumnList.ConcatColumns(otherColumnList, (a, b) => a.ConcatDataColumns(b)); - private static List> ConcatIntColumns(this IReadOnlyList> thisColumnList, IReadOnlyList> otherColumnList) + public static List> ConcatIntColumns(this IReadOnlyList> thisColumnList, IReadOnlyList> otherColumnList) => thisColumnList.ConcatColumns(otherColumnList, (a, b) => new NamedBuffer(a.GetTypedData().Concat(b.GetTypedData()).ToArray(), a.Name)); diff --git a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs index 3fd3a49b..81f3efe3 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnExtensions.Reflection.cs @@ -16,10 +16,10 @@ public static string GetDataColumnNameTypePrefix(this Type type) throw new Exception($"{nameof(GetDataColumnNameTypePrefix)} error: no matching data column name prefix for {type}"); } - private static bool CanSerializeAsStringColumn(this Type type) + public static bool CanSerializeAsStringColumn(this Type type) => type == typeof(string); - private static bool CanSerializeAsDataColumn(this Type type) + public static bool CanSerializeAsDataColumn(this Type type) => DataColumnTypes.Contains(type); public static ValueSerializationStrategy GetValueSerializationStrategy(this Type type) @@ -60,7 +60,7 @@ public static string GetSerializedValueColumnName(this FieldInfo fieldInfo) return $"{typePrefix}{fieldInfo.GetSerializedValueName()}"; } - private static bool IsRelationType(this Type t) + public static bool IsRelationType(this Type t) => t.Name == "Relation`1"; public static Type RelationTypeParameter(this Type t) diff --git a/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs b/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs index 6de4e200..531e3619 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnExtensions.cs @@ -7,7 +7,7 @@ namespace Vim.Format { public static partial class ColumnExtensions { - private static readonly IReadOnlyCollection AllColumnInfos + public static readonly IReadOnlyCollection AllColumnInfos = new[] { new ColumnInfo(ColumnType.IndexColumn, VimConstants.IndexColumnNameTypePrefix, typeof(int)), @@ -19,10 +19,7 @@ private 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); - - private static readonly IReadOnlyDictionary DataColumnTypeToPrefixMap + public static readonly IReadOnlyDictionary DataColumnTypeToPrefixMap = AllColumnInfos .Where(t => t.ColumnType == ColumnType.DataColumn) .SelectMany(t => t.RelatedTypes.Select(type => (Type: type, t.TypePrefix))) @@ -31,13 +28,13 @@ private static readonly IReadOnlyDictionary DataColumnTypeToPrefix public static readonly ISet DataColumnTypes = new HashSet(AllColumnInfos.Where(t => t.ColumnType == ColumnType.DataColumn).SelectMany(t => t.RelatedTypes)); - private static readonly ISet DataColumnNameTypePrefixes + public static readonly ISet DataColumnNameTypePrefixes = new HashSet(AllColumnInfos.Where(t => t.ColumnType == ColumnType.DataColumn).Select(t => t.TypePrefix)); - private static readonly Regex DataColumnTypePrefixRegex + public static readonly Regex DataColumnTypePrefixRegex = new Regex($@"^(?:{string.Join("|", DataColumnNameTypePrefixes)})"); - private static bool TryGetDataColumnNameTypePrefix(string columnName, out string typePrefix) + public static bool TryGetDataColumnNameTypePrefix(string columnName, out string typePrefix) { typePrefix = null; if (string.IsNullOrEmpty(columnName)) @@ -51,7 +48,7 @@ private static bool TryGetDataColumnNameTypePrefix(string columnName, out string public static bool IsDataColumnName(string columnName) => TryGetDataColumnNameTypePrefix(columnName, out _); - private const string RelatedTableNameFieldNameSeparator = ":"; + public const string RelatedTableNameFieldNameSeparator = ":"; public static string GetIndexColumnName(string relatedTableName, string localFieldName) => VimConstants.IndexColumnNameTypePrefix + relatedTableName + RelatedTableNameFieldNameSeparator + localFieldName; diff --git a/src/cs/vim/Vim.Format.Core/ColumnInfo.cs b/src/cs/vim/Vim.Format.Core/ColumnInfo.cs index 29497c36..dde7b439 100644 --- a/src/cs/vim/Vim.Format.Core/ColumnInfo.cs +++ b/src/cs/vim/Vim.Format.Core/ColumnInfo.cs @@ -15,16 +15,16 @@ public class ColumnInfo { public readonly ColumnType ColumnType; public readonly string TypePrefix; - private readonly Type _serializedType; - private readonly ISet _castTypes; + public readonly Type SerializedType; + public readonly ISet CastTypes; public ColumnInfo(ColumnType columnType, string typePrefix, Type serializedType, params Type[] castTypes) { - (ColumnType, TypePrefix, _serializedType) = (columnType, typePrefix, serializedType); - _castTypes = new HashSet(castTypes); + (ColumnType, TypePrefix, SerializedType) = (columnType, typePrefix, serializedType); + CastTypes = new HashSet(castTypes); } public IEnumerable RelatedTypes - => _castTypes.Prepend(_serializedType); + => CastTypes.Prepend(SerializedType); } } diff --git a/src/cs/vim/Vim.Format.Core/Document.cs b/src/cs/vim/Vim.Format.Core/Document.cs index df4673d8..a24b8be3 100644 --- a/src/cs/vim/Vim.Format.Core/Document.cs +++ b/src/cs/vim/Vim.Format.Core/Document.cs @@ -1,6 +1,9 @@ -using System.Collections.Generic; +using System.Collections; +using System.Collections.Generic; using System.Linq; +using System.Xml.Linq; using Vim.BFastLib; +using Vim.Format; using Vim.G3d; using Vim.Util; @@ -13,23 +16,62 @@ public Document(SerializableDocument document) { _Document = document; Header = _Document.Header; - Geometry = _Document.Geometry; + GeometryNext = _Document.GeometryNext; StringTable = _Document.StringTable; + EntityTables = _Document.EntityTables.ToDictionary( et => et.Name, et => new EntityTable(this, et)); + + + Assets = _Document.Assets.ToDictionary(et => et.Name, et => et); + + var tables = document.EntityTables.ToDictionary( + et => et.Name, + et => new EntityTable(this, et)); + _bim = new Bim(tables, _Document.StringTable); } - private SerializableDocument _Document { get; } + Bim _bim; + + public int TableCount => _bim.TableCount; + + public EntityTable GetTable(string name) + => _bim.GetTable(name); + + public IEnumerable TableNames => _bim.TableNames; + public IEnumerable Tables => _bim.Tables; + public VimSchema GetSchema() => VimSchema.Create(_Document); + + public string FileName => _Document.FileName; + public SerializableDocument _Document { get; } public SerializableHeader Header { get; } - public IDictionary EntityTables { get; } - public IDictionary Assets { get; } - public IList StringTable { get; } + public Dictionary EntityTables { get; } + public Dictionary Assets { get; } + public string[] StringTable { get; } public string GetString(int index) => StringTable.ElementAtOrDefault(index); - public G3d.G3D Geometry { get; } + public G3dVim GeometryNext { get; } + } +} - public EntityTable GetTable(string name) - => EntityTables.GetOrDefault(name); +public class Bim +{ + private string[] _strings; + private Dictionary _tables { get; } + + public Bim(Dictionary tables, string[] strings) + { + _tables = tables; + _strings = strings; } + + public int TableCount => Tables.Count(); + + public EntityTable GetTable(string name) + => _tables.GetOrDefault(name); + + public IEnumerable Tables => _tables.Values; + public IEnumerable TableNames => Tables.Select(e => e.Name); + } diff --git a/src/cs/vim/Vim.Format.Core/DocumentBuilder.cs b/src/cs/vim/Vim.Format.Core/DocumentBuilder.cs index efd07c5e..c5b1e68b 100644 --- a/src/cs/vim/Vim.Format.Core/DocumentBuilder.cs +++ b/src/cs/vim/Vim.Format.Core/DocumentBuilder.cs @@ -6,7 +6,7 @@ using Vim.BFastLib; using System.IO; using Vim.Util; -using Vim.G3d; +using Vim.Format.Geometry; namespace Vim.Format { @@ -15,11 +15,8 @@ public partial class DocumentBuilder public readonly SerializableHeader Header; public readonly Dictionary Tables = new Dictionary(); public readonly Dictionary Assets = new Dictionary(); - //public readonly List Meshes = new List(); - //public readonly List Instances = new List(); - //public readonly List Shapes = new List(); - //public readonly List Materials = new List(); public readonly G3dBuilder Geometry = new G3dBuilder(); + public bool UseColors { get; set; } public DocumentBuilder( @@ -52,13 +49,13 @@ public DocumentBuilder AddAsset(string name, byte[] asset) return this; } - public DocumentBuilder AddMesh(SubdividedMesh mesh) + public DocumentBuilder AddMesh(VimMesh mesh) { Geometry.AddMesh(mesh); return this; } - public DocumentBuilder AddMeshes(IEnumerable meshes) + public DocumentBuilder AddMeshes(IEnumerable meshes) { foreach (var m in meshes) { @@ -88,7 +85,7 @@ public DocumentBuilder AddInstance(Matrix4x4 transform, int meshIndex, int paren return this; } - public DocumentBuilder AddMaterials(IEnumerable materials) + public DocumentBuilder AddMaterials(IEnumerable materials) { foreach (var material in materials) { diff --git a/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs b/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs index aa9c046b..aa425c18 100644 --- a/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/DocumentBuilderExtensions.cs @@ -1,59 +1,38 @@ using System.Collections.Generic; using System.Linq; using Vim.BFastLib; -using Vim.Format.Geometry; -using Vim.G3d; -using static Vim.Format.DocumentBuilder; namespace Vim.Format { public static class DocumentBuilderExtensions { - public static IMesh ToIMesh(this SubdividedMesh gb) - => gb.Vertices.ToArray().TriMesh( - gb.Indices.ToArray(), - submeshMaterials: gb.SubmeshMaterials.ToArray()); - - 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()); - - private static void CreateTableCopy(this DocumentBuilder db, EntityTable table, List nodeIndexRemapping = null) + 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) + foreach (var col in table.IndexColumns) { tb.AddIndexColumn(col.Name, col.GetTypedData().RemapData(nodeIndexRemapping)); } - foreach (var col in table.DataColumns.Values) + foreach (var col in table.DataColumns) { tb.AddDataColumn(col.Name, col.CopyDataColumn(nodeIndexRemapping)); } - foreach (var col in table.StringColumns.Values) + foreach (var col in table.StringColumns) { - var strings = col.GetTypedData().Select(i => table.Document.StringTable.ElementAtOrDefault(i, null)); + var strings = col.GetTypedData().Select(i => table.Document.GetString(i)); tb.AddStringColumn(col.Name, strings.ToArray().RemapData(nodeIndexRemapping)); } + + return tb; } - public static void CopyTablesFrom(this DocumentBuilder db, Document doc, List nodeIndexRemapping = null) + public static DocumentBuilder CopyTablesFrom(this DocumentBuilder db, Document doc, List nodeIndexRemapping = null) { - foreach (var table in doc.EntityTables.Values) + foreach (var table in doc.Tables) { var name = table.Name; @@ -63,34 +42,10 @@ public static void CopyTablesFrom(this DocumentBuilder db, Document doc, List stringLookup) - { - var table = new SerializableEntityTable - { - // Set the table name - Name = tb.Name, - - // Convert the columns to named buffers - IndexColumns = tb.IndexColumns - .Select(kv => kv.Value.ToNamedBuffer(kv.Key)) - .ToList(), - DataColumns = tb.DataColumns - .Select(kv => kv.Value.ToNamedBuffer(kv.Key) as INamedBuffer) - .ToList(), - StringColumns = tb.StringColumns - .Select(kv => kv.Value - .Select(s => stringLookup[s ?? string.Empty]) - .ToArray() - .ToNamedBuffer(kv.Key)) - .ToList(), - }; - - table.ValidateColumnRowsAreAligned(); - - return table; + return db; } + + } } diff --git a/src/cs/vim/Vim.Format.Core/DocumentBuilderTypes.cs b/src/cs/vim/Vim.Format.Core/DocumentBuilderTypes.cs index b401a915..7d1a5853 100644 --- a/src/cs/vim/Vim.Format.Core/DocumentBuilderTypes.cs +++ b/src/cs/vim/Vim.Format.Core/DocumentBuilderTypes.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Vim.Format.Geometry; using Vim.G3d; using Vim.Math3d; @@ -23,12 +24,12 @@ public class Shape public float Width; } - public class Material + public class Material : IMaterial { //RGBA - public Vector4 Color; - public float Glossiness; - public float Smoothness; + public Vector4 Color { get; set; } + public float Glossiness { get; set; } + public float Smoothness { get; set; } } /// @@ -36,19 +37,19 @@ public class Material /// public class Mesh { - private readonly List _vertices; + protected List _vertices = new List(); public IReadOnlyList Vertices => _vertices; - private readonly List _indices; + protected List _indices = new List(); public IReadOnlyList Indices => _indices; - private List _faceMaterials; + protected List _faceMaterials = new List(); public IReadOnlyList FaceMaterials => _faceMaterials; - private readonly List _colors; + protected List _colors = new List(); public IReadOnlyList Colors => _colors; - private readonly List _uvs; + protected List _uvs = new List(); public IReadOnlyList UVs => _uvs; public Mesh(List vertices = null, List indices = null, List faceMaterials = null, List colors = null, List uvs = null) @@ -71,15 +72,16 @@ public Mesh(List vertices = null, List indices = null, List f _uvs = uvs ?? new List(); } + public void SetMeshMaterial(int material) => _faceMaterials = Enumerable.Repeat(material, _indices.Count / 3).ToList(); - public void AppendFaces(IList indices, IList materials) + public void AppendFaces(int[] indices, int[] materials) { - if (indices.Count != materials.Count * 3) - throw new Exception("index.Count must be material.Count*3"); + if (indices.Length != materials.Length * 3) + throw new Exception("index.Length must be material.Length*3"); - for (var i = 0; i < materials.Count; i++) + for (var i = 0; i < materials.Length; i++) { var index = i * 3; AppendFace(indices[index], indices[index + 1], indices[index + 2], materials[i]); @@ -100,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/EntityColumnLoaderAttribute.cs b/src/cs/vim/Vim.Format.Core/EntityColumnLoaderAttribute.cs index cb555817..000c6d12 100644 --- a/src/cs/vim/Vim.Format.Core/EntityColumnLoaderAttribute.cs +++ b/src/cs/vim/Vim.Format.Core/EntityColumnLoaderAttribute.cs @@ -5,7 +5,7 @@ namespace Vim.Format { [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] - public class EntityColumnLoaderAttribute : Attribute + public partial class EntityColumnLoaderAttribute : Attribute { /// /// The name of the serialized column. diff --git a/src/cs/vim/Vim.Format.Core/EntityTable.cs b/src/cs/vim/Vim.Format.Core/EntityTable.cs index 2236e83b..fe58b5b7 100644 --- a/src/cs/vim/Vim.Format.Core/EntityTable.cs +++ b/src/cs/vim/Vim.Format.Core/EntityTable.cs @@ -1,7 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; using Vim.BFastLib; +using System.Linq; +using System.Collections.Generic; using Vim.Util; namespace Vim.Format @@ -14,9 +14,9 @@ public EntityTable(Document document, SerializableEntityTable entityTable) _EntityTable = entityTable; Name = _EntityTable.Name; - 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); + _dataColumns = _EntityTable.DataColumns.ToDictionary( c => c.Name, c => c); + _indexColumns = _EntityTable.IndexColumns.ToDictionary(c => c.Name, c => c); + _stringColumns = _EntityTable.StringColumns.ToDictionary(c => c.Name, c => c); NumRows = Columns.FirstOrDefault()?.NumElements() ?? 0; Columns.ValidateColumnRowsAreAligned(); @@ -26,42 +26,60 @@ public EntityTable(Document document, SerializableEntityTable entityTable) public Document Document { get; } public string Name { get; } public int NumRows { get; } - public IDictionary DataColumns { get; } - public IDictionary> StringColumns { get; } - public IDictionary> IndexColumns { get; } - public IList Columns - => DataColumns.Values - .Concat(IndexColumns.Values.Select(x => (INamedBuffer)x)) - .Concat(StringColumns.Values.Select(x => (INamedBuffer)x)) - .ToArray(); - - public IList GetIndexColumnValues(string columnName) - => IndexColumns.GetOrDefault(columnName)?.GetColumnValues(); - - public IList GetStringColumnValues(string columnName) - => StringColumns.GetOrDefault(columnName) - ?.GetColumnValues() - ?.Select(Document.GetString) - .ToArray(); - - public IList GetDataColumnValues(string columnName) where T : unmanaged + private Dictionary _dataColumns { get; } + private Dictionary> _stringColumns { get; } + private Dictionary> _indexColumns { get; } + + + // Data + public bool HasDataColumns(string name) => _dataColumns.ContainsKey(name); + public IEnumerable DataColumns => _dataColumns.Values; + public IEnumerable DataColumnNames => _dataColumns.Keys; + + // Strings + public IEnumerable> StringColumns => _stringColumns.Values; + public bool HasStringColumn(string name) => _stringColumns.ContainsKey(name); + public NamedBuffer GetStringColumn(string name) => _stringColumns.GetOrDefault(name); + public IEnumerable StringColumnNames => _stringColumns.Keys; + + //Data + public IEnumerable> IndexColumns => _indexColumns.Values; + public IEnumerable IndexColumnNames => _indexColumns.Keys; + public bool HasIndexColumn(string name) => _indexColumns.ContainsKey(name); + public NamedBuffer GetIndexColumn(string name) => _indexColumns.GetOrDefault(name); + + + public IEnumerable Columns + => DataColumns + .Concat(IndexColumns.Select(x => (INamedBuffer)x)) + .Concat(StringColumns.Select(x => (INamedBuffer)x)); + + public int[] GetIndexColumnValues(string columnName) + => GetIndexColumn(columnName)?.AsArray(); + + public string[] GetStringColumnValues(string columnName) + => _stringColumns.GetOrDefault(columnName) + ?.AsArray() + ?.Select(Document.GetString).ToArray(); + + public T[] GetDataColumnValues(string columnName) where T : unmanaged { var type = typeof(T); if (!ColumnExtensions.DataColumnTypes.Contains(type)) throw new Exception($"{nameof(GetDataColumnValues)} error - unsupported data column type {type}"); - var namedBuffer = DataColumns.GetOrDefault(columnName); + var namedBuffer = _dataColumns.GetOrDefault(columnName); if (namedBuffer == null) return null; if (type == typeof(short)) - return namedBuffer.GetColumnValues().Select(i => (short)i) as IList; + return namedBuffer.AsArray().Select(i => (short)i) as T[]; if (type == typeof(bool)) - return namedBuffer.GetColumnValues().Select(b => b != 0) as IList; + return namedBuffer.AsArray().Select(b => b != 0) as T[]; - return namedBuffer.GetColumnValues(); + return namedBuffer.AsArray(); } } } diff --git a/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs b/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs index 91add434..2205500a 100644 --- a/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs +++ b/src/cs/vim/Vim.Format.Core/EntityTableBuilder.cs @@ -17,19 +17,20 @@ public class EntityTableBuilder public EntityTableBuilder(string name) => Name = name; - private void UpdateOrValidateRows(int n) + public EntityTableBuilder UpdateOrValidateRows(int n) { if (NumRows == 0) NumRows = n; else if (NumRows != n) throw new Exception($"Value count {n} does not match the expected number of rows {NumRows}"); + return this; } - private static void ValidateHasDataColumnPrefix(string columnName) + public void ValidateHasDataColumnPrefix(string columnName) { if (!ColumnExtensions.IsDataColumnName(columnName)) throw new Exception($"{nameof(columnName)} {columnName} does not begin with a data column prefix"); } - private static void ValidateHasPrefix(string columnName, string expectedPrefix) + public void ValidateHasPrefix(string columnName, string expectedPrefix) { if (!columnName.StartsWith(expectedPrefix)) throw new Exception($"{nameof(columnName)} {columnName} must start with {expectedPrefix}"); @@ -43,10 +44,8 @@ public EntityTableBuilder AddIndexColumn(string columnName, int[] indices) return this; } - public void AddIndexColumn(string columnName, IEnumerable ids) - { - AddIndexColumn(columnName, ids.ToArray()); - } + public EntityTableBuilder AddIndexColumn(string columnName, IEnumerable ids) + => AddIndexColumn(columnName, ids.ToArray()); public EntityTableBuilder AddStringColumn(string columnName, string[] values) { @@ -98,6 +97,33 @@ public IEnumerable GetAllStrings() => StringColumns.Values.SelectMany(sc => sc) .Where(x => x != null); + public SerializableEntityTable ToSerializableEntityTable(IReadOnlyDictionary stringLookup) + { + var table = new SerializableEntityTable + { + // Set the table name + Name = Name, + + // Convert the columns to named buffers + IndexColumns = IndexColumns + .Select(kv => kv.Value.ToNamedBuffer(kv.Key)) + .ToList(), + DataColumns = DataColumns + .Select(kv => kv.Value.ToNamedBuffer(kv.Key) as INamedBuffer) + .ToList(), + StringColumns = StringColumns + .Select(kv => kv.Value + .Select(s => stringLookup[s ?? string.Empty]) + .ToArray() + .ToNamedBuffer(kv.Key)) + .ToList(), + }; + + table.ValidateColumnRowsAreAligned(); + + return table; + } + public void Clear() { NumRows = 0; diff --git a/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs b/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs index 5ba63d55..cb7b4a60 100644 --- a/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs +++ b/src/cs/vim/Vim.Format.Core/EntityTable_v2.cs @@ -37,7 +37,7 @@ public EntityTable_v2( string[] stringBuffer) { Name = et.Name; - Columns = et.ValidateColumnRowsAreAligned().ToArray(); + Columns = et.ValidateColumnRowsAreAligned().AllColumns.ToArray(); RowCount = Columns.FirstOrDefault()?.NumElements() ?? 0; foreach (var column in et.IndexColumns) diff --git a/src/cs/vim/Vim.Format.Core/G3dBuilder.cs b/src/cs/vim/Vim.Format.Core/G3dBuilder.cs index 3033844c..5ca97441 100644 --- a/src/cs/vim/Vim.Format.Core/G3dBuilder.cs +++ b/src/cs/vim/Vim.Format.Core/G3dBuilder.cs @@ -4,22 +4,23 @@ 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 _meshes = new List(); private readonly List _instances = new List(); private readonly List _shapes = new List(); - private readonly List _materials = new List(); + private readonly List _materials = new List(); - public void AddMesh(SubdividedMesh mesh) + public void AddMesh(VimMesh mesh) { _meshes.Add(mesh); } - public void AddMeshes(IEnumerable meshes) + public void AddMeshes(IEnumerable meshes) { foreach (var mesh in meshes) { @@ -45,7 +46,7 @@ public void AddShape(Shape shape) _shapes.Add(shape); } - public void AddMaterial(Material material) + public void AddMaterial(IMaterial material) { _materials.Add(material); } @@ -55,27 +56,27 @@ public void AddMaterial(Material material) public int MaterialCount => _materials.Count; public int ShapeCount => _shapes.Count; - public SubdividedMesh GetMesh(int index) => _meshes[index]; + public VimMesh GetMesh(int index) => _meshes[index]; public AABox GetBox(int meshIndex) { - return AABox.Create(_meshes[meshIndex].Vertices); + return AABox.Create(_meshes[meshIndex].vertices); } public int[] GetVertexCounts() { - return _meshes.Select(m => m.Vertices.Count).ToArray(); + return _meshes.Select(m => m.vertices.Length).ToArray(); } public int[] GetFaceCounts() { - return _meshes.Select(m => m.Indices.Count / 3).ToArray(); + return _meshes.Select(m => m.NumFaces).ToArray(); } public BFast ToBFast() { var bfast = new BFast(); - var totalSubmeshCount = _meshes.Select(s => s.SubmeshesIndexOffset.Count).Sum(); + var totalSubmeshCount = _meshes.Select(s => s.SubmeshCount).Sum(); // Compute the Vertex offsets and index offsets var meshVertexOffsets = new int[_meshes.Count]; @@ -87,20 +88,20 @@ public BFast ToBFast() 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; + 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.SubmeshesIndexOffset) + foreach (var sub in geo.submeshIndexOffsets) { submeshIndexOffsets[subIndex++] = sub + previousIndexCount; } - previousIndexCount += geo.Indices.Count; + previousIndexCount += geo.indices.Length; } // Compute the shape vertex offsets @@ -111,11 +112,11 @@ public BFast ToBFast() 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.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.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)); diff --git a/src/cs/vim/Vim.Format.Core/Geometry/Bounded.cs b/src/cs/vim/Vim.Format.Core/Geometry/Bounded.cs index 51079b50..ca2571de 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/Bounded.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/Bounded.cs @@ -6,4 +6,10 @@ public interface IBounded { AABox Bounds { get; } } + + public static class Bounded + { + public static AABox UpdateBounds(this IBounded self, AABox box) + => box.Merge(self.Bounds); + } } diff --git a/src/cs/vim/Vim.Format.Core/Geometry/CatmullClark.cs b/src/cs/vim/Vim.Format.Core/Geometry/CatmullClark.cs new file mode 100644 index 00000000..30142230 --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/Geometry/CatmullClark.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using Vim.Math3d; + +namespace Vim.Format.Geometry +{ + // This is used to index an edge from any two vertices. + // Vertex order doesnt matter. + public class GeometryEdgeKey : IEquatable + { + public int Vertex0 { get; } + public int Vertex1 { get; } + + public GeometryEdgeKey(int v0, int v1) + { + Vertex0 = Math.Min(v0, v1); + Vertex1 = Math.Max(v0, v1); + } + + public override int GetHashCode() + => Hash.Combine(Vertex0.GetHashCode(), Vertex1.GetHashCode()); + + public override bool Equals(object obj) + => obj is GeometryEdgeKey && Equals((GeometryEdgeKey)obj); + + public bool Equals(GeometryEdgeKey p) + => Vertex0 == p.Vertex0 && Vertex1 == p.Vertex1; + } + + // Stores information about an edge in a mesh. + public class GeometryEdge + { + public Vector3 EdgePoint { get; set; } + public int EdgePointIndex { get; set; } = -1; + public int Face0 { get; set; } = -1; + public int Face1 { get; set; } = -1; + } + + public static class CatmullClarkSmoothing + { + public static Dictionary CreateEdgeMap(this VimMesh geometry) + { + var globalEdgeIndex = 0; + var edgeMap = new Dictionary(); + var currentIndex = 0; + for (var faceIndex = 0; faceIndex < geometry.NumFaces; faceIndex++) + { + var faceSize = 3; + for (var edgeIndex = 0; edgeIndex < faceSize; edgeIndex++) + { + var geometryEdgeKey = new GeometryEdgeKey(geometry.indices[currentIndex + edgeIndex], geometry.indices[currentIndex + (edgeIndex + 1) % faceSize]); + if (edgeMap.ContainsKey(geometryEdgeKey)) + { + edgeMap[geometryEdgeKey].Face1 = faceIndex; + } + else + { + var geometryEdge = new GeometryEdge(); + geometryEdge.EdgePointIndex = globalEdgeIndex++; + geometryEdge.Face0 = faceIndex; + edgeMap[geometryEdgeKey] = geometryEdge; + } + } + currentIndex += faceSize; + } + + return edgeMap; + } + + public static VimMesh CatmullClark(this VimMesh geometry, float smoothing = 0.0f) + { + var edgeMap = geometry.CreateEdgeMap(); + + var numQuads = geometry.NumFaces * 3; + var numVertices = geometry.vertices.Length + edgeMap.Count + geometry.NumFaces; + + var facePoints = new Vector3[geometry.NumFaces]; + 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.Length]; + + foreach (var edge in edgeMap) + { + if (edge.Value.Face0 == -1 || edge.Value.Face1 == -1) + { + edgeVertices[edge.Key.Vertex0] = true; + edgeVertices[edge.Key.Vertex1] = true; + } + } + + // For each face, add a face point + // Set each face point to be the average of all original points for the respective face. + // For each edge, add an edge point. + // Set each edge point to be the average of the two neighbouring face points and its two original endpoints. + // For each face point, add an edge for every edge of the face, connecting the face point to each edge point for the face. + // For each original point P, take the average F of all n (recently created) face points for faces touching P, and take + // the average R of all n edge midpoints for (original) edges touching P, where each edge midpoint is the average of its + // two endpoint vertices (not to be confused with new "edge points" above). Move each original point to the point + // + // (F + 2 R + ( n − 3 ) P) / n + // + // This is the barycenter of P, R and F with respective weights (n − 3), 2 and 1. + // + // Connect each new vertex point to the new edge points of all original edges incident on the original vertex. + // Define new faces as enclosed by edges. + + CalculateFaceCentroidPoints(geometry, facePoints, vertexNumFaces, vertexFPoints); + CalculateEdgePoints(geometry, edgeMap, facePoints, vertexNumEdges, vertexRPoints, smoothing); + CalculateVertexPoints(geometry, vertexNumFaces, vertexFPoints, vertexNumEdges, vertexRPoints, newVertices, edgeVertices, smoothing); + + var facePointStartIndex = geometry.vertices.Length; + var edgePointStartIndex = facePointStartIndex + facePoints.Length; + + // Copy all points into a single buffer + for (var i = 0; i < facePoints.Length; i++) + { + newVertices[facePointStartIndex + i] = facePoints[i]; + } + foreach (var edge in edgeMap) + { + newVertices[edgePointStartIndex + edge.Value.EdgePointIndex] = edge.Value.EdgePoint; + } + + return GenerateMesh(geometry, newVertices, newIndices, edgeMap, facePointStartIndex, edgePointStartIndex, numQuads); + } + + public static void CalculateFaceCentroidPoints(VimMesh geometry, Vector3[] outFacePoints, float[] outVertexNumFaces, Vector3[] outVertexFPoints) + { + var currentVertexIndex = 0; + for (var faceIndex = 0; faceIndex < geometry.NumFaces; faceIndex++) + { + var faceSize = geometry.NumCornersPerFace; + + var facePoint = new Vector3(); + + for (var edgeIndex = 0; edgeIndex < faceSize; edgeIndex++) + { + var vertexIndex = geometry.indices[currentVertexIndex + edgeIndex]; + facePoint += geometry.vertices[vertexIndex]; + } + + facePoint /= faceSize; + + outFacePoints[faceIndex] = facePoint; + + for (var edgeIndex = 0; edgeIndex < faceSize; edgeIndex++) + { + var vertexIndex = geometry.indices[currentVertexIndex + edgeIndex]; + outVertexNumFaces[vertexIndex]++; + outVertexFPoints[vertexIndex] += facePoint; + } + + currentVertexIndex += faceSize; + } + } + + public static void CalculateEdgePoints(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 edgePoint = middlePoint; + + if (edge.Value.Face0 >= 0 && edge.Value.Face1 >= 0) + { + edgePoint += facePoints[edge.Value.Face0]; + edgePoint += facePoints[edge.Value.Face1]; + n += 2.0f; + } + + middlePoint /= 2.0f; + edgePoint /= n; + + edge.Value.EdgePoint = edgePoint * smoothing + middlePoint * (1.0f - smoothing); + + outVertexNumEdges[edge.Key.Vertex0]++; + outVertexNumEdges[edge.Key.Vertex1]++; + outVertexRPoints[edge.Key.Vertex0] += edge.Value.EdgePoint; + outVertexRPoints[edge.Key.Vertex1] += edge.Value.EdgePoint; + } + } + + public static void CalculateVertexPoints(VimMesh geometry, float[] vertexNumFaces, Vector3[] vertexFPoints, float[] vertexNumEdges, Vector3[] vertexRPoints, Vector3[] outNewVertices, bool[] edgeVertices, float smoothing) + { + for (var index = 0; index < geometry.vertices.Length; index++) + { + var numFaces = vertexNumFaces[index]; + var numEdges = vertexNumEdges[index]; + var newVertex = (vertexFPoints[index] / numFaces + 2.0f * vertexRPoints[index] / numEdges + (numFaces - 3.0f) * geometry.vertices[index]) / numFaces; + + outNewVertices[index] = edgeVertices[index] ? geometry.vertices[index] : smoothing * newVertex + (1.0f - smoothing) * geometry.vertices[index]; + } + } + + public static VimMesh GenerateMesh(VimMesh geometry, Vector3[] newVertices, int[] newIndices, Dictionary edgeMap, int facePointStartIndex, int edgePointStartIndex, int numQuads) + { + var currentNewVertexIndex = 0; + var currentOldVertexIndex = 0; + for (var faceIndex = 0; faceIndex < geometry.NumFaces; faceIndex++) + { + var faceSize = geometry.NumCornersPerFace; + + for (var i = 0; i < faceSize; i++) + { + var prevFaceVertexIndex = geometry.indices[currentOldVertexIndex + i]; + var faceVertexIndex = geometry.indices[currentOldVertexIndex + (i + 1) % faceSize]; + var nextFaceVertexIndex = geometry.indices[currentOldVertexIndex + (i + 2) % faceSize]; + + var edgeKey0 = new GeometryEdgeKey(prevFaceVertexIndex, faceVertexIndex); + var edgeKey1 = new GeometryEdgeKey(faceVertexIndex, nextFaceVertexIndex); + + newIndices[currentNewVertexIndex++] = faceIndex + facePointStartIndex; + newIndices[currentNewVertexIndex++] = edgeMap[edgeKey0].EdgePointIndex + edgePointStartIndex; + newIndices[currentNewVertexIndex++] = faceVertexIndex; + newIndices[currentNewVertexIndex++] = edgeMap[edgeKey1].EdgePointIndex + edgePointStartIndex; + } + + currentOldVertexIndex += faceSize; + } + + return 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 new file mode 100644 index 00000000..b35ac47d --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/Geometry/GeometryCuttingUtils.cs @@ -0,0 +1,127 @@ +using Vim.Math3d; + +namespace Vim.Format.Geometry +{ + public static class GeometryCuttingUtils + { + // Fins the intersection between two lines. + // Returns true if they intersect + // t and u are the distances of the intersection point along the two line + public static bool LineLineIntersection(Vector2 line1Origin, Vector2 line1Direction, Vector2 line2Origin, Vector2 line2Direction, out float t, out float u, float epsilon = 0.01f) + { + var line1P2 = line1Origin + line1Direction; + var line2P2 = line2Origin + line2Direction; + + var x1 = line1Origin.X; + var y1 = line1Origin.Y; + var x2 = line1P2.X; + var y2 = line1P2.Y; + var x3 = line2Origin.X; + var y3 = line2Origin.Y; + var x4 = line2P2.X; + var y4 = line2P2.Y; + + var denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); + + if (denominator.Abs() < epsilon) + { + t = 0.0f; + u = 0.0f; + return false; + } + + var tNeumerator = (x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4); + var uNeumerator = (x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3); + + t = tNeumerator / denominator; + u = -uNeumerator / denominator; + + return true; + } + + // Returns the distance between two lines + // t and u are the distances if the intersection points along the two lines + public static float LineLineDistance(Vector2 line1A, Vector2 line1B, Vector2 line2A, Vector2 line2B, out float t, out float u, float epsilon = 0.0000001f) + { + var x1 = line1A.X; + var y1 = line1A.Y; + var x2 = line1B.X; + var y2 = line1B.Y; + var x3 = line2A.X; + var y3 = line2A.Y; + var x4 = line2B.X; + var y4 = line2B.Y; + + var denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); + + if (denominator.Abs() >= epsilon) + { + // Lines are not parallel, they should intersect nicely + var tNeumerator = (x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4); + var uNeumerator = (x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3); + + t = tNeumerator / denominator; + u = -uNeumerator / denominator; + + var e = 0.0; + if (t >= -e && t <= 1.0 + e && u >= -e && u <= 1.0 + e) + { + t = t.Clamp(0.0f, 1.0f); + u = u.Clamp(0.0f, 1.0f); + return 0; + } + } + + // Parallel or non intersecting lines - default to point to line checks + + u = 0.0f; + var minDistance = LinePointDistance(line1A, line1B, line2A, out t); + var distance = LinePointDistance(line1A, line1B, line2B, out var closestPoint); + if (distance < minDistance) + { + minDistance = distance; + t = closestPoint; + u = 1.0f; + } + + distance = LinePointDistance(line2A, line2B, line1A, out closestPoint); + if (distance < minDistance) + { + minDistance = distance; + u = closestPoint; + t = 0.0f; + } + + distance = LinePointDistance(line2A, line2B, line1B, out closestPoint); + if (distance < minDistance) + { + minDistance = distance; + u = closestPoint; + t = 1.0f; + } + + return minDistance; + } + + // Returns the distance between a line and a point. + // t is the distance along the line of the closest point + public static float LinePointDistance(Vector2 v, Vector2 w, Vector2 p, out float t) + { + // Return minimum distance between line segment vw and point p + var l2 = (v - w).LengthSquared(); // i.e. |w-v|^2 - avoid a sqrt + if (l2 == 0.0f) // v == w case + { + t = 0.5f; + return (p - v).Length(); + } + + // Consider the line extending the segment, parameterized as v + t (w - v). + // We find projection of point p onto the line. + // It falls where t = [(p-v) . (w-v)] / |w-v|^2 + // We clamp t from [0,1] to handle points outside the segment vw. + t = ((p - v).Dot(w - v) / l2).Clamp(0.0f, 1.0f); + var closestPoint = v + t * (w - v); // Projection falls on the segment + return (p - closestPoint).Length(); + } + } +} 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 a861f08a..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/IMesh.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using Vim.G3d; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - /// - /// This is the interface for triangle meshes. - /// - public interface IMesh : - IGeometryAttributes, - ITransformable3D - { - IList Vertices { get; } - IList Indices { get; } - IList VertexColors { get; } - IList VertexNormals { get; } - IList VertexUvs { get; } - - IList SubmeshMaterials { get; } - IList SubmeshIndexOffsets { get; } - IList 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 c15e2032..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/IScene.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.Generic; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - /// - /// An IScene is a generic representation of a 3D scene graph. - /// - public interface IScene - { - IList Nodes { get; } - IList 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; } - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/KdTree.cs b/src/cs/vim/Vim.Format.Core/Geometry/KdTree.cs index fb03edf0..153e1a31 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/KdTree.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/KdTree.cs @@ -34,7 +34,7 @@ public void Add(T item) { if (IsSplit) throw new Exception("Cannot add meshes after split"); - Box = Box.Merge(item.Bounds); + Box = item.UpdateBounds(Box); Items.Add(item); } 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 9c37c614..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/MeshExtensions.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Vim.G3d; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - public static class MeshExtensions - { - public static IMesh ToIMesh(this IEnumerable self) - { - var tmp = new GeometryAttributes(self); - switch (tmp.NumCornersPerFace) - { - case 3: - return new TriMesh(tmp.Attributes); - case 4: - return new QuadMesh(tmp.Attributes).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(); - - public static IList 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 - .SelectMany((indexCount, i) => Enumerable.Repeat(mesh.SubmeshMaterials[i], indexCount / numCornersPerFace)) - .ToArray(); - } - - public static IMesh Merge(this IList meshes) - => meshes.Select(m => (IGeometryAttributes)m).ToArray().Merge().ToIMesh(); - - public static IMesh Merge(this IEnumerable meshes) - => meshes.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 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 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.ToIndexAttribute()); - } - - public static AABox BoundingBox(this IMesh mesh) - => AABox.Create(mesh.Vertices); - - public static Vector3 Center(this IMesh mesh) - => mesh.BoundingBox().Center; - - 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 IList Triangles(this IMesh mesh) - => mesh.NumFaces.Select(mesh.Triangle); - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs b/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs index e8944164..0478b940 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/MeshOptimization.cs @@ -1,25 +1,27 @@ -using Vim.LinqArray; +using System.Collections.Generic; +using System.Linq; using Vim.Math3d; namespace Vim.Format.Geometry { + /// /// This class is used to compare quickly two meshes within a lookup table (e.g. Dictionary, HashTable). /// it looks at the positions of each corner, and the number of faces, and assures that the object ID /// and material IDs are the same. /// When using a class within a dictionary or hash table, the equals operator is called frequently. - /// By converting an IMesh to a MeshHash we minimize the amount of comparisons done. It becomes + /// By converting an VimMesh to a MeshHash we minimize the amount of comparisons done. It becomes /// possible, but highly unlikely that two different meshes would have the same hash. /// public class MeshHash { - public IMesh Mesh; + public VimMesh Mesh; public float Tolerance; - public readonly int NumFaces; - public readonly int NumVertices; - public readonly int TopologyHash; - public readonly Int3 BoxExtents; - public readonly Int3 BoxMin; + public int NumFaces; + public int NumVertices; + public int TopologyHash; + public Int3 BoxExtents; + public Int3 BoxMin; public int Round(float f) => (int)(f / Tolerance); @@ -27,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); + TopologyHash = Hash.Combine(mesh.indices); var box = mesh.BoundingBox(); BoxMin = Round(box.Min); BoxExtents = Round(box.Extent); @@ -53,4 +55,9 @@ public override int GetHashCode() => Hash.Combine(NumFaces, NumVertices, TopologyHash, BoxMin.GetHashCode(), BoxExtents.GetHashCode()); } + public static class Optimization + { + 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 new file mode 100644 index 00000000..6acbe6b1 --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/Geometry/PerimeterProjection.cs @@ -0,0 +1,595 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Vim.Math3d; + +namespace Vim.Format.Geometry +{ + public static class PerimeterProjection + { + + public static List> GeneratePerimeter(this VimMesh mesh, Vector3 planeNormal, float degenerateSegmentEpsilon = 10.0f, float edgeLoopThreshold = 1e-6f) + { + var q = GetClusterRotation(planeNormal.Normalize(), Vector3.UnitZ); + + var segments = mesh.BuildBoundarySegments(null, + input => input.Transform(q).ToVector2()); + + segments = segments.SplitIntersectingCurves(edgeLoopThreshold * 50); + + var graph = segments.BuildConnectionGraph(edgeLoopThreshold * 50); + var loops = graph.FindGraphLoops(edgeLoopThreshold * 50); + + loops = loops.RemoveDuplicatedVertices(edgeLoopThreshold * 50); + + while (true) + { + var newLoops = loops.RemoveDegenerateSegmentsLooping(edgeLoopThreshold); + if (newLoops.Count == loops.Count) + { + var notEqual = false; + for (var i = 0; i < newLoops.Count; i++) + { + if (newLoops[i].Count != loops[i].Count) + { + notEqual = true; + break; + } + } + + if (!notEqual) + { + break; + } + } + + loops = newLoops; + } + + return loops; + } + + public static bool IsNaN(this Quaternion q) + => q.X.IsNaN() || q.Y.IsNaN() || q.Z.IsNaN() || q.W.IsNaN(); + + // Finds all of the intersections between the curves and splits them + // Also adds a pseudo intersection at the left-most point, this gives the FindGraphLoops + // algorithm a good starting point + public static List> SplitIntersectingCurves(this List> curves, float threshold) + { + var thresholdSquared = threshold * threshold; + var curveList = new LinkedList>(curves); + + var intersections = new Dictionary, List>>>(); // Madness in great ones must not unwatched go. + var leftMostPoint = Vector2.MaxValue; + List leftMostCurve = null; + var leftMostIntersectionPoint = 0.0f; + + foreach (var currentCurveA in curveList) + { + for (var i = 0; i < currentCurveA.Count; i++) + { + var point = currentCurveA[i]; + if (point.X < leftMostPoint.X || (point.X == leftMostPoint.X && point.Y < leftMostPoint.Y)) + { + leftMostPoint = point; + leftMostIntersectionPoint = i; + leftMostCurve = currentCurveA; + } + } + + foreach (var currentCurveB in curveList) + { + if (currentCurveA != currentCurveB) + { + float t, u; + var distance = IntersectCurves(currentCurveA, currentCurveB, out t, out u); + if (distance < threshold) + { + if (!intersections.ContainsKey(currentCurveA)) + { + intersections[currentCurveA] = new List>>(); + } + if (!intersections.ContainsKey(currentCurveB)) + { + intersections[currentCurveB] = new List>>(); + } + + intersections[currentCurveA].Add(new Tuple>(distance, t, currentCurveB)); + intersections[currentCurveB].Add(new Tuple>(distance, u, currentCurveA)); + } + } + } + } + + // split the left most curve so that FindGraphLoops has a good starting point + if (leftMostIntersectionPoint != 0.0 && leftMostIntersectionPoint != leftMostCurve.Count - 1) + { + intersections[leftMostCurve].Add(new Tuple>(0.0f, leftMostIntersectionPoint, null)); + } + + var edgeLoops = new List>(); + foreach (var intersection in intersections) + { + var splits = intersection.Value.Select(x => x.Item2); + + edgeLoops.AddRange(intersection.Key.SplitCurve(splits)); + } + + return edgeLoops; + } + + // Cuts the curve between min and max and returns the part in between + public static List> SplitCurve(this List curve, IEnumerable splits) + { + var result = new List>(); + if (splits.Count() == 0) + { + result.Add(curve); + return result; + } + + splits = splits.OrderBy(x => x).ToList(); + var newSplits = new List(); + if (splits.First() != 0.0f) + { + newSplits.Add(0.0f); + } + + newSplits.AddRange(splits); + + if (splits.Last() < curve.Count - 1) + { + newSplits.Add(curve.Count - 1); + } + + for (var i = 0; i < newSplits.Count - 1; i++) + { + var min = newSplits[i]; + var max = newSplits[i + 1]; + + var clampedCurve = curve.ClampCurve(min, max); + + if (clampedCurve.CurveLength() > 1e-5f) + { + result.Add(clampedCurve); + } + } + + return result; + } + + + // Cuts the curve between min and max and returns the part in between + public static List ClampCurve(this List curve, float min, float max) + { + var floorMin = (float)Math.Floor(min); + var floorMax = (float)Math.Floor(max); + var fracMin = min - floorMin; + var fracMax = max - floorMax; + + if (fracMin == 0.0 && floorMin > 0) + { + floorMin--; + fracMin++; + } + if (fracMax == 0.0 && floorMax > 0) + { + floorMax--; + fracMax++; + } + + var indexMin = (int)floorMin; + var indexMax = (int)floorMax; + + var result = new List(); + if (fracMin != 1.0) + { + result.Add(curve[indexMin].Lerp(curve[indexMin + 1], (float)fracMin)); + } + for (var i = indexMin + 1; i <= indexMax; i++) + { + result.Add(curve[i]); + } + result.Add(curve[indexMax].Lerp(curve[indexMax + 1], (float)fracMax)); + + return result; + } + + // Returns the length of the curve + public static float CurveLength(this List curve) + { + var curveLength = 0.0f; + for (var x = 0; x < curve.Count - 1; x++) + { + curveLength += (curve[x] - curve[x + 1]).Length(); + } + + return curveLength; + } + + // Returns min distance between piecewise linear curves A and B. + // t = distance along curve A + // u = distance along curve B + // TODO: Return multiple intersections within a tolerance + public static float IntersectCurves(List segmentsA, List segmentsB, out float t, out float u) + { + var minDistance = float.MaxValue; + t = 0.0f; + u = 0.0f; + + for (var segmentAIndex = 0; segmentAIndex < segmentsA.Count - 1; segmentAIndex++) + { + var v1a = segmentsA[segmentAIndex]; + var v1b = segmentsA[segmentAIndex + 1]; + + for (var segmentBIndex = 0; segmentBIndex < segmentsB.Count - 1; segmentBIndex++) + { + var v2a = segmentsB[segmentBIndex]; + var v2b = segmentsB[segmentBIndex + 1]; + + float fracT, fracU; + var distance = GeometryCuttingUtils.LineLineDistance(v1a, v1b, v2a, v2b, out fracT, out fracU); + + if (distance < minDistance) + { + minDistance = distance; + t = segmentAIndex + fracT; + u = segmentBIndex + fracU; + } + } + } + + return minDistance; + } + + public static List> RemoveDuplicatedVertices(this List> edgeLoops, float threshold) + { + var thresholdSquared = threshold * threshold; + foreach (var edgeLoop in edgeLoops) + { + for (var i = 0; i < edgeLoop.Count; i++) + { + if ((edgeLoop[i] - edgeLoop[(i + 1) % edgeLoop.Count]).LengthSquared() <= thresholdSquared) + { + edgeLoop.RemoveAt(i); + i--; + } + } + } + + return edgeLoops; + } + + public static Quaternion GetClusterRotation(Vector3 clusterNormal, Vector3 alignTo, float tolerance = 0.001f) + { + var c = clusterNormal.Cross(alignTo); + var d = clusterNormal.Dot(alignTo); + + if (d >= 1f - tolerance) + return Quaternion.Identity; + + if (d <= -(1f - tolerance)) + { + // Need an axis of rotation that is perpendicular to the two input vectors + var axis = clusterNormal.Cross(alignTo.Dot(Vector3.UnitY) > 0.999f ? Vector3.UnitZ : Vector3.UnitY); + return Quaternion.CreateFromAxisAngle(axis, (float)Math.PI); + } + + var s = (float)Math.Sqrt((1.0f + d) * 2.0f); + var invs = 1.0f / s; + + var q = new Quaternion(c * invs, s * 0.5f); + q = q.Normalize(); + + Debug.Assert(!q.IsNaN()); + return q; + } + + public static void AddEdgeToDictionary(Dictionary, int> d, int x, int y) + { + var key = new Tuple(x, y); + if (!d.ContainsKey(key)) + d[key] = 1; + else + d[key]++; + } + + private class EdgeEqualityComparer : IEqualityComparer> + { + public bool Equals(Tuple x, Tuple y) + => ((x.Item1 == y.Item1) && (x.Item2 == y.Item2)) || ((x.Item1 == y.Item2) && (x.Item2 == y.Item1)); + + public int GetHashCode(Tuple obj) + => obj.Item1.GetHashCode() ^ obj.Item2.GetHashCode(); + } + + public static Dictionary, int> BuildEdgeDictionary(this VimMesh mesh) + { + var edges = new Dictionary, int>(new EdgeEqualityComparer()); + + var indices = mesh.indices; + + for (var i = 0; i < indices.Length; i += 3) + { + var i0 = indices[i + 0]; + var i1 = indices[i + 1]; + var i2 = indices[i + 2]; + + AddEdgeToDictionary(edges, i0, i1); + AddEdgeToDictionary(edges, i1, i2); + AddEdgeToDictionary(edges, i2, i0); + } + + return edges; + } + + public static List> BuildBoundarySegments(this VimMesh mesh, Dictionary, int> edgeDictionary, Func transform) + { + var segments = new List>(); + + var indices = mesh.indices; + var vertices = mesh.vertices; + + for (var i = 0; i < indices.Length; i += 3) + { + var i0 = indices[i + 0]; + var i1 = indices[i + 1]; + var i2 = indices[i + 2]; + + var v0 = transform(vertices[i0]); + var v1 = transform(vertices[i1]); + var v2 = transform(vertices[i2]); + + if (edgeDictionary == null || edgeDictionary[new Tuple(i0, i1)] != 2) segments.Add(new List { v0, v1 }); + if (edgeDictionary == null || edgeDictionary[new Tuple(i1, i2)] != 2) segments.Add(new List { v1, v2 }); + if (edgeDictionary == null || edgeDictionary[new Tuple(i2, i0)] != 2) segments.Add(new List { v2, v0 }); + } + + return segments; + } + + // Builds a node connection graph from a list of curves + public static Dictionary, List>>> BuildConnectionGraph(this List> curves, float threshold) + { + var connectionGraph = new Dictionary, List>>>(); + var reverseCurves = new Dictionary, List>(); + var thresholdSquared = threshold * threshold; + + var pointList = new Vector2[curves.Count]; + for (var i = 0; i < curves.Count; i++) + { + var curve = curves[i]; + connectionGraph[curve.First()] = new List, List>>(); + connectionGraph[curve.Last()] = new List, List>>(); + + var reverseCurve = new List(curve); + reverseCurve.Reverse(); + reverseCurves[curve] = reverseCurve; + reverseCurves[reverseCurve] = curve; + } + + foreach (var point in connectionGraph) + { + foreach (var curve in curves) + { + var distanceSquared = (curve.First() - point.Key).LengthSquared(); + if (distanceSquared <= thresholdSquared) + { + point.Value.Add(new Tuple, List>(curve, reverseCurves[curve])); + } + + distanceSquared = (curve.Last() - point.Key).LengthSquared(); + if (distanceSquared <= thresholdSquared) + { + point.Value.Add(new Tuple, List>(reverseCurves[curve], curve)); + } + } + } + + return connectionGraph; + } + + // Build a loop around the perimeter of the graph + // TODO: Handle multiple loops + public static List> FindGraphLoops(this Dictionary, List>>> connectionGraph, float threshold) + { + // Start from the left most point, build the perimeter by selecting the connection with the minimum clockwise angle + var thresholdSquared = threshold * threshold; + var leftMostPoint = Vector2.MaxValue; + foreach (var item in connectionGraph) + { + if (item.Value.Count < 2) + { + continue; + } + if (item.Key.X < leftMostPoint.X || (item.Key.X == leftMostPoint.X && item.Key.Y < leftMostPoint.Y)) + { + leftMostPoint = item.Key; + } + } + + var visitedNodes = new Dictionary, bool>(); + + var result = new List>(); + var loop = new List(); + var currentPointLast = leftMostPoint + new Vector2(0, 1); + var currentPoint = leftMostPoint; + var previousPoint = currentPoint; + + var segmentCount = 0; + while (true) + { + if (!connectionGraph.ContainsKey(currentPoint)) + { + // Failed :( + result.Add(loop); + return result; + } + + var list = connectionGraph[currentPoint].Where(x => + { + if (connectionGraph[x.Item1.Last()].Count <= 1) + { + // Don't go down a dead end, this might need improving with some kind of backtracking when a dead end was found + return false; + } + + if (visitedNodes.ContainsKey(x.Item1)) + { + return false; + } + + return true; + }); + var sortedList = list.OrderBy(x => + { + var s1 = (currentPoint - currentPointLast).Normalize(); + var s2 = (x.Item1[1] - x.Item1[0]).Normalize(); + var angle = (float)Math.Atan2(s1.Cross(s2), s1.Dot(s2)); + return angle; + }).ToList(); + + if (sortedList.Count == 0) + { + // Failed :( + result.Add(loop); + return result; + } + + var nextConnection = sortedList.First(); + visitedNodes[nextConnection.Item1] = true; + visitedNodes[nextConnection.Item2] = true; + segmentCount++; + loop.AddRange(nextConnection.Item1); + previousPoint = currentPoint; + currentPoint = nextConnection.Item1.Last(); + currentPointLast = nextConnection.Item1[nextConnection.Item1.Count - 2]; + + if ((currentPoint - loop[0]).LengthSquared() < thresholdSquared) + { + result.Add(loop); + break; + } + + if (segmentCount > connectionGraph.Count) + { + // Failed :( + result.Add(loop); + return result; + } + } + + return result; + } + + // Remove degenerate segments + public static List> RemoveDegenerateSegments(this List> edgeLoops, float epsilon = 1e-10f) + { + var result = new List>(); + foreach (var edgeLoop in edgeLoops) + { + if (edgeLoop.Count < 2) + { + //result.Add(edgeLoop); + continue; + } + + var newEdgeLoop = new List(edgeLoop); + for (var i = 1; i < newEdgeLoop.Count - 1; i++) + { + var v0 = newEdgeLoop[i - 1]; + var v1 = newEdgeLoop[i]; + var v2 = newEdgeLoop[i + 1]; + var area = (v1 - v0).Cross(v1 - v2).Abs(); + if (area < epsilon) + { + newEdgeLoop.RemoveAt(i); + i--; + } + } + + if (newEdgeLoop.Count < 2) + { + continue; + } + + result.Add(newEdgeLoop); + } + + return result; + } + public static List> RemoveDegenerateSegmentsLooping(this List> edgeLoops, float epsilon = 1e-10f) + { + var result = new List>(); + foreach (var edgeLoop in edgeLoops) + { + if (edgeLoop.Count < 2) + { + //result.Add(edgeLoop); + continue; + } + + var newEdgeLoop = new List(edgeLoop); + for (var i = 0; i < newEdgeLoop.Count; i++) + { + var v0 = newEdgeLoop[(i - 1 + newEdgeLoop.Count) % newEdgeLoop.Count]; + var v1 = newEdgeLoop[i]; + var v2 = newEdgeLoop[(i + 1) % newEdgeLoop.Count]; + var area = (v1 - v0).Cross(v1 - v2).Abs(); + if (area < epsilon) + { + newEdgeLoop.RemoveAt(i); + i--; + } + } + + if (newEdgeLoop.Count < 2) + { + continue; + } + + result.Add(newEdgeLoop); + } + + return result; + } + + public static List> RemoveDegenerateSegments(this List> edgeLoops, float epsilon, Func faceFunction) + { + var result = new List>(); + foreach (var edgeLoop in edgeLoops) + { + if (edgeLoop.Count < 2) + { + //result.Add(edgeLoop); + continue; + } + + var newEdgeLoop = new List(edgeLoop); + for (var i = 1; i < newEdgeLoop.Count - 1; i++) + { + var v0 = faceFunction(newEdgeLoop[i - 1]); + var v1 = faceFunction(newEdgeLoop[i]); + var v2 = faceFunction(newEdgeLoop[i + 1]); + var area = (v1 - v0).Cross(v1 - v2).LengthSquared(); + if (area < epsilon * epsilon) + { + newEdgeLoop.RemoveAt(i); + i--; + } + } + + if (newEdgeLoop.Count < 2) + { + continue; + } + + result.Add(newEdgeLoop); + } + + return result; + } + } +} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs b/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs index 6479490a..ae24622f 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/Primitives.cs @@ -1,96 +1,155 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; -using Vim.G3d; using Vim.Math3d; 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 IList vertices, - IList indices = null, - IList uvs = null, - IList colors = null, - IList materials = null, - IList submeshMaterials = null) - => TriMesh( - vertices?.ToPositionAttribute(), - indices?.ToIndexAttribute(), - uvs?.ToVertexUvAttribute(), - materials?.ToFaceMaterialAttribute(), - colors?.ToVertexColorAttribute(), - submeshMaterials?.ToSubmeshMaterialAttribute() - ); - - public static IMesh TriMesh(this IList vertices, IList indices = null, params GeometryAttribute[] attributes) - => new GeometryAttribute[] { - vertices?.ToPositionAttribute(), - indices?.ToIndexAttribute(), - }.Concat(attributes).ToIMesh(); - - 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) + }; + + 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 VimMesh CreateCube(AABox box) + { + return CreateCube().Scale(box.Extent).Translate(box.Center); + } + + private static float Sqrt2 = 2.0f.Sqrt(); + public static VimMesh CreateTetrahedron() + { + var vertices = new[] { - 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 vertices.TriMesh(indices); - } + 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) + }; + 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); } - public static IMesh CubeFaceted + // see: https://github.com/mrdoob/three.js/blob/9ef27d1af7809fa4d9943f8d4c4644e365ab6d2d/src/geometries/TorusBufferGeometry.js#L52 + public static Vector3 TorusFunction(Vector2 uv, float radius, float tube) { - get + uv *= Math3d.Constants.TwoPi; + return new Vector3( + (radius + tube * uv.Y.Cos()) * uv.X.Cos(), + (radius + tube * uv.Y.Cos()) * uv.X.Sin(), + tube * uv.Y.Sin()); + } + + public static VimMesh Torus(float radius, float tubeRadius, int uSegs, int vSegs) + => QuadMesh(uv => TorusFunction(uv, radius, tubeRadius), uSegs, vSegs); + + // see: https://github.com/mrdoob/three.js/blob/9ef27d1af7809fa4d9943f8d4c4644e365ab6d2d/src/geometries/SphereBufferGeometry.js#L76 + public static Vector3 SphereFunction(Vector2 uv, float radius) + => new Vector3( + (float)(-radius * Math.Cos(uv.X * Math3d.Constants.TwoPi) * Math.Sin(uv.Y * Math3d.Constants.Pi)), + (float)(radius * Math.Cos(uv.Y * Math3d.Constants.Pi)), + (float)(radius * Math.Sin(uv.X * Math3d.Constants.TwoPi) * Math.Sin(uv.Y * Math3d.Constants.Pi))); + + public static VimMesh Sphere(float radius, int uSegs, int vSegs) + => QuadMesh(uv => SphereFunction(uv, radius), uSegs, vSegs); + + /// + /// Returns a collection of circular points. + /// + public static Vector2[] CirclePoints(float radius, int numPoints) + => CirclePoints(numPoints).Select(x => x * radius).ToArray(); + + 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()); + + /// + /// Computes the indices of a quad mesh astrip. + /// + public static int[] ComputeQuadMeshStripIndices(int usegs, int vsegs, bool wrapUSegs = false, bool wrapVSegs = false) + { + var indices = new List(); + + var maxUSegs = wrapUSegs ? usegs : usegs + 1; + var maxVSegs = wrapVSegs ? vsegs : vsegs + 1; + + for (var i = 0; i < vsegs; ++i) { - var cube = Cube; - return cube.Indices.Select(i => cube.Vertices[i]).ToArray().TriMesh(cube.Indices.Count.Range()); + var rowA = i * maxUSegs; + var rowB = ((i + 1) % maxVSegs) * maxUSegs; + + for (var j = 0; j < usegs; ++j) + { + var colA = j; + var colB = (j + 1) % maxUSegs; + + indices.Add(rowA + colA); + indices.Add(rowA + colB); + indices.Add(rowB + colB); + indices.Add(rowB + colA); + } } + + return indices.ToArray(); } /// /// Returns the index buffer of a quad mesh strip. /// Returns an empty array if either numRowPoints or numPointsPerRow is less than 2. /// - public static List QuadMeshStripIndicesFromPointRows( + public static int[] QuadMeshStripIndicesFromPointRows( int numPointRows, int numPointsPerRow, bool clockwise = false) @@ -146,7 +205,7 @@ public static List QuadMeshStripIndicesFromPointRows( } } - return indices; + return indices.ToArray(); } public static int[] TriMeshCylinderCapIndices(int numEdgeVertices) @@ -191,5 +250,34 @@ public static int[] TriMeshCylinderCapIndices(int numEdgeVertices) return indices.ToArray(); } + + /// + /// Creates a quad mesh given a mapping from 2 space to 3 space + /// + 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 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; + var maxVSegs = wrapVSegs ? vsegs : vsegs + 1; + + for (var i = 0; i < maxVSegs; ++i) + { + var v = (float)i / vsegs; + for (var j = 0; j < maxUSegs; ++j) + { + var u = (float)j / usegs; + verts.Add(f(new Vector2(u, v))); + } + } + var indices = ComputeQuadMeshStripIndices(usegs, vsegs, wrapUSegs, wrapVSegs); + + 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 fc58e000..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/SceneExtensions.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -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 MergedGeometry(this IScene scene) - => scene.Nodes.MergedGeometry(); - - public static IMesh MergedGeometry(this IEnumerable nodes) - => nodes.Where(n => n.GetMesh() != null).Select(TransformedMesh).Merge(); - - public static IEnumerable AllVertices(this IScene scene) - => scene.TransformedMeshes().SelectMany(g => g.Vertices); - - public static AABox BoundingBox(this IScene scene) - => AABox.Create(scene.AllVertices()); - - public static IList TransformedVertices(this ISceneNode node) - => node.TransformedMesh()?.Vertices; - - public static AABox TransformedBoundingBox(this ISceneNode node) - => AABox.Create(node.TransformedVertices()); - } -} 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 2bde8a3e..f61d3bbe 100644 --- a/src/cs/vim/Vim.Format.Core/Geometry/Validation.cs +++ b/src/cs/vim/Vim.Format.Core/Geometry/Validation.cs @@ -1,30 +1,27 @@ using System; -using System.Collections.Generic; -using System.Linq; -using Vim.G3d; using Vim.BFastLib; namespace Vim.Format.Geometry { public static class Validation { - private static void ValidateTableRows(this Document doc) + public static void ValidateTableRows(this Document doc) { - foreach (var et in doc.EntityTables.Values.ToArray()) + foreach (var et in doc.Tables) { - foreach (var c in et.IndexColumns.Values.ToArray()) + foreach (var c in et.IndexColumns) { if (c.Array.Length != et.NumRows) throw new Exception($"Expected array length {c.Array.Length} of column {c.Name} to be the same as number of rows {et.NumRows}"); } - foreach (var c in et.DataColumns.Values.ToArray()) + foreach (var c in et.DataColumns) { if (c.NumElements() != et.NumRows) throw new Exception($"Expected array length {c.NumElements()} of column {c.Name} to be the same as number of rows {et.NumRows}"); } - foreach (var c in et.StringColumns.Values.ToArray()) + foreach (var c in et.StringColumns) { if (c.Array.Length != et.NumRows) throw new Exception($"Expected array length {c.Array.Length} of column {c.Name} to be the same as number of rows {et.NumRows}"); @@ -32,11 +29,11 @@ private static void ValidateTableRows(this Document doc) } } - private static void ValidateIndexColumns(this Document doc) + public static void ValidateIndexColumns(this Document doc) { - foreach (var et in doc.EntityTables.Values.ToArray()) + foreach (var et in doc.Tables) { - foreach (var ic in et.IndexColumns.Values) + foreach (var ic in et.IndexColumns) { var table = ic.GetRelatedTable(doc); if (table == null) @@ -45,35 +42,7 @@ private static void ValidateIndexColumns(this Document doc) } } - private static string[] RequiredAttributeNames => new [] - { - // Vertices - CommonAttributes.Position, - CommonAttributes.Index, - - // Meshes - CommonAttributes.MeshSubmeshOffset, - - // Submeshes - CommonAttributes.SubmeshIndexOffset, - - // Instances - CommonAttributes.InstanceMesh, - CommonAttributes.InstanceTransform, - }; - - private static void ValidateGeometryAttributes(this Document doc) - { - var attributes = doc.Geometry.Attributes; - var attributeNameSet = new HashSet(attributes.Select(a => a.Name)); - foreach (var attributeName in RequiredAttributeNames) - { - if (!attributeNameSet.Contains(attributeName)) - throw new Exception($"Required attribute {attributeName} was not found."); - } - } - - private static void ValidateAssets(this Document doc) + public static void ValidateAssets(this Document doc) { 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). - - private static void ValidateIndices(this IMesh mesh) - { - foreach (var index in mesh.Indices) - { - if (index < 0 || index >= mesh.NumVertices) - throw new Exception($"Invalid mesh index: {index}. Expected a value greater or equal to 0 and less than {mesh.NumVertices}"); - } - } - - public static void Validate(this IMesh mesh) - { - mesh.ValidateIndices(); - } } } diff --git a/src/cs/vim/Vim.Format.Core/Geometry/VimMaterial.cs b/src/cs/vim/Vim.Format.Core/Geometry/VimMaterial.cs deleted file mode 100644 index 4799219f..00000000 --- a/src/cs/vim/Vim.Format.Core/Geometry/VimMaterial.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Vim.G3d; -using Vim.Math3d; - -namespace Vim.Format.Geometry -{ - public interface IMaterial - { - Vector4 Color { get; } - float Smoothness { get; } - float Glossiness { get; } - } - - public class VimMaterial : IMaterial - { - public G3dMaterial Material; - public VimMaterial(G3dMaterial material) => Material = material; - public Vector4 Color => Material.Color; - public float Smoothness => Material.Smoothness; - public float Glossiness => Material.Glossiness; - } -} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/VimMaterialNext.cs b/src/cs/vim/Vim.Format.Core/Geometry/VimMaterialNext.cs new file mode 100644 index 00000000..07ee1f2e --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/Geometry/VimMaterialNext.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using Vim.G3d; +using Vim.Math3d; + +namespace Vim.Format.Geometry +{ + public interface IMaterial + { + Vector4 Color { get; } + float Smoothness { get; } + float Glossiness { get; } + } + + + public class VimMaterialNext : IMaterial + { + public G3dVim g3d; + public int index; + + public static IEnumerable FromG3d(G3dVim g3d) + { + for(var i =0; i < g3d.GetMaterialCount(); i++) + { + yield return new VimMaterialNext(g3d, i); + } + } + + public Vector4 Color => g3d.MaterialColors[index]; + public float Smoothness => g3d.MaterialSmoothness[index]; + public float Glossiness => g3d.MaterialGlossiness[index]; + public VimMaterialNext(G3dVim g3d, int index) + { + this.g3d = g3d; + this.index = index; + } + } +} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/VimMesh.cs b/src/cs/vim/Vim.Format.Core/Geometry/VimMesh.cs new file mode 100644 index 00000000..b80c1a0f --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/Geometry/VimMesh.cs @@ -0,0 +1,444 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Vim.G3d; +using Vim.Math3d; + +namespace Vim.Format.Geometry +{ + public class VimMesh : ITransformable3D + { + public int SubmeshCount => submeshIndexCounts.Length; + + public int NumCornersPerFace => 3; + + public int NumVertices => vertices.Length; + + public int NumCorners => indices.Length; + + public int NumFaces => indices.Length / 3; + + + public int[] indices; + public Vector3[] vertices; + public int[] submeshIndexOffsets; + public int[] submeshMaterials; + public int[] submeshIndexCounts; + + public int GetIndexCount() => indices.Length; + public int GetVertexCount() => vertices.Length; + + + public VimMesh() + { + + } + + public VimMesh(Vector3[] vertices) + : this(Enumerable.Range(0, vertices.Length).ToArray(), vertices) + { + + } + + public VimMesh( + int[] indices, + Vector3[] vertices, + int[] submeshIndexOffsets = null, + int[] submeshMaterials = null, + int[] submeshIndexCounts = null + ) + { + this.indices = indices; + this.vertices = vertices; + + this.submeshIndexOffsets = submeshIndexOffsets ?? new int[1] { 0 }; + this.submeshMaterials = submeshMaterials ?? new int[1] { -1 }; + this.submeshIndexCounts = submeshIndexCounts ?? ComputeCounts(this.submeshIndexOffsets, this.indices.Length); + } + + public G3dVim ToG3d() + { + return new G3dVim( + indices: indices, + positions: vertices, + instanceMeshes: new[] { 0 }, + instanceTransforms: new[] { Matrix4x4.Identity }, + instanceParents: null, + instanceFlags: null, + meshSubmeshOffsets: new[] { 0 }, + submeshIndexOffsets: new[] { 0 }, + submeshMaterials: new[] { -1 }, + materialColors: null, + materialGlossiness: null, + materialSmoothness: null, + shapeVertices: null, + shapeColors: null, + shapeVertexOffsets: null, + shapeWidths: null); + } + + private static int[] ComputeCounts(int[] offsets, int max) + { + var result = new int[offsets.Length]; + for (var i = 0; i < result.Length - 1; i++) + { + result[i] = offsets[i + 1] - offsets[i]; + } + var last = result.Length - 1; + result[last] = max - offsets[last]; + return result; + } + + + public VimMesh(int indexCount, int vertexCount, int submeshCount) + { + indices = new int[indexCount]; + vertices = new Vector3[vertexCount]; + submeshIndexOffsets = new int[submeshCount]; + submeshMaterials = new int[submeshCount]; + submeshIndexCounts = new int[submeshCount]; + } + + public VimMesh Clone() + { + var mesh = new VimMesh( + indices, + vertices, + submeshIndexOffsets, + submeshMaterials + ); + return mesh; + } + public VimMesh Transform(Matrix4x4 mat) + { + var mesh = Clone(); + + for (var i = 0; i < vertices.Length; i++) + { + mesh.vertices[i] = vertices[i].Transform(mat); + } + + return mesh; + } + + public static VimMesh FromG3d(G3dVim g3d, int index) + { + var mesh = new VimMesh(); + + var vStart = g3d.GetMeshVertexStart(index); + var vEnd = g3d.GetMeshVertexEnd(index); + mesh.vertices = new Vector3[vEnd - vStart]; + for (var i = 0; i < mesh.vertices.Length; i++) + { + var v = vStart + i; + mesh.vertices[i] = g3d.Positions[v]; + } + + var iStart = g3d.GetMeshIndexStart(index); + var iEnd = g3d.GetMeshIndexEnd(index); + mesh.indices = new int[iEnd - iStart]; + for (var i = 0; i < mesh.indices.Length; i++) + { + var j = iStart + i; + mesh.indices[i] = g3d.Indices[j] - vStart; + } + + var sStart = g3d.GetMeshSubmeshStart(index); + var sEnd = g3d.GetMeshSubmeshEnd(index); + mesh.submeshIndexOffsets = new int[sEnd - sStart]; + mesh.submeshMaterials = new int[sEnd - sStart]; + mesh.submeshIndexCounts = new int[sEnd - sStart]; + + for (var i = 0; i < mesh.submeshMaterials.Length; i++) + { + var s = sStart + i; + mesh.submeshIndexOffsets[i] = g3d.SubmeshIndexOffsets[s] - iStart; + mesh.submeshMaterials[i] = g3d.SubmeshMaterials[s]; + mesh.submeshIndexCounts[i] = g3d.GetSubmeshIndexCount(s); + } + + return mesh; + } + + public static VimMesh FromG3d(G3dVim g3d) + { + var mesh = new VimMesh( + g3d.Indices, + g3d.Positions, + g3d.SubmeshIndexOffsets, + g3d.SubmeshMaterials + ); + + return mesh; + } + + public static VimMesh FromQuad(int[] indices, Vector3[] vertices) + { + if (indices.Length % 4 != 0) + { + throw new ArgumentException("Indices count should be a multiple of 4."); + } + var faceCount = indices.Length / 4; + var triIndices = new int[faceCount * 6]; + var cur = 0; + for (var i = 0; i < faceCount; ++i) + { + triIndices[cur++] = indices[i * 4 + 0]; + triIndices[cur++] = indices[i * 4 + 1]; + triIndices[cur++] = indices[i * 4 + 2]; + triIndices[cur++] = indices[i * 4 + 0]; + triIndices[cur++] = indices[i * 4 + 2]; + triIndices[cur++] = indices[i * 4 + 3]; + } + return new VimMesh(triIndices, vertices); + } + + public static IEnumerable GetAllMeshes(G3dVim g3d) + { + return Enumerable.Range(0, g3d.GetMeshCount()).Select(i => FromG3d(g3d, i)); + } + + public void SetVertices(Vector3[] vertices) + { + this.vertices = vertices; + } + public void SetIndices(int[] indices) + { + this.indices = indices; + } + + public void Validate() + { + //TODO: Validate better + ValidateIndices(); + } + private void ValidateIndices() + { + foreach (var index in indices) + { + if (index < 0 || index >= NumVertices) + throw new Exception($"Invalid mesh index: {index}. Expected a value greater or equal to 0 and less than {NumVertices}"); + } + } + } + + public static class MeshCommonExtensions + { + public static VimMesh ReverseWindingOrder(this VimMesh mesh) + { + var result = mesh.Clone(); + var count = mesh.indices.Length; + var indices = new int[count]; + for (var i = 0; i < count; i += 3) + { + indices[i + 0] = mesh.indices[i + 2]; + indices[i + 1] = mesh.indices[i + 1]; + indices[i + 2] = mesh.indices[i + 0]; + } + result.SetIndices(indices); + return result; + } + + + public static int[] GetFaceMaterials(this VimMesh mesh) + { + // SubmeshIndexOffsets: [0, A, B] + // SubmeshIndexCount: [X, Y, Z] + // SubmeshMaterials: [L, M, N] + // --- + // FaceMaterials: [...Repeat(L, X / 3), ...Repeat(M, Y / 3), ...Repeat(N, Z / 3)] <-- divide by 3 for the number of corners per Triangular face + var numCornersPerFace = mesh.NumCornersPerFace; + return mesh.submeshIndexCounts + .SelectMany((indexCount, i) => Enumerable.Repeat(mesh.submeshMaterials[i], indexCount / numCornersPerFace)) + .ToArray(); + } + + public static VimMesh Merge2(this VimMesh mesh, params VimMesh[] others) + { + var meshes = Enumerable.Empty() + .Append(mesh) + .Concat(others) + .ToArray(); + + return meshes.Merge(); + } + + public static VimMesh Merge(this VimMesh[] meshes) + { + void Merge(int[] from, int[] to, int offset, int increment) + { + for (var i = 0; i < from.Length; i++) + { + to[i + offset] = from[i] + increment; + } + } + + // Init arrays + var indexCount = meshes.Sum(m => m.indices.Length); + var vertexCount = meshes.Sum(m => m.vertices.Length); + var submeshCount = meshes.Sum(m => m.submeshIndexOffsets.Length); + var result = new VimMesh(indexCount, vertexCount, submeshCount); + + var indexOffset = 0; + var vertexOffset = 0; + var submeshOffset = 0; + // Copy and merge meshes + for (var m = 0; m < meshes.Length; m++) + { + var mesh = meshes[m]; + + Merge(mesh.indices, result.indices, indexOffset, vertexOffset); + mesh.vertices.CopyTo(result.vertices, vertexOffset); + mesh.submeshMaterials.CopyTo(result.submeshMaterials, submeshOffset); + mesh.submeshIndexCounts.CopyTo(result.submeshIndexCounts, submeshOffset); + Merge(mesh.submeshIndexOffsets, result.submeshIndexOffsets, submeshOffset, indexOffset); + + indexOffset += mesh.indices.Length; + vertexOffset += mesh.vertices.Length; + submeshOffset += mesh.submeshIndexOffsets.Length; + + } + return result; + } + + public static VimMesh[] SplitSubmeshes(this VimMesh mesh) + { + return null; + } + + 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 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 bool GeometryEquals(this VimMesh mesh, VimMesh other, float tolerance = Math3d.Constants.Tolerance) + { + if (!mesh.indices.SequenceEqual(other.indices)) + return false; + + if (!mesh.submeshIndexOffsets.SequenceEqual(other.submeshIndexOffsets)) + return false; + + if (!mesh.submeshMaterials.SequenceEqual(other.submeshMaterials)) + return false; + + if (!mesh.submeshIndexCounts.SequenceEqual(other.submeshIndexCounts)) + return false; + + if (mesh.vertices.Length != other.vertices.Length) + return false; + + for (var i = 0; i < mesh.vertices.Length; i++) + { + if (!mesh.vertices[i].AlmostEquals(other.vertices[i], tolerance)) + return false; + } + + return true; + } + + public static (int mat, VimMesh mesh)[] SplitByMaterial(this VimMesh mesh) + { + var map = mesh.GroupSubmeshesByMaterials(); + + var result = new (int, VimMesh)[map.Length]; + if (map.Length == 1) + { + result[0] = (mesh.submeshMaterials[0], mesh); + return result; + } + + for (var i = 0; i < map.Length; i++) + { + var (mat, subs) = map[i]; + var pick = mesh.PickSubmeshes(subs); + result[i] = (mat, pick); + } + return result; + } + + public static VimMesh PickSubmeshes(this VimMesh mesh, IList submeshes) + { + var map = mesh.GroupSubmeshesByMaterials(); + + // Allocate arrays of the final sizes + var indexCount = submeshes.Sum(s => mesh.submeshIndexCounts[s]); + var result = new VimMesh(indexCount, indexCount, submeshes.Count); + + var indexOffset = 0; + var index = 0; + for (var s = 0; s < submeshes.Count; s++) + { + var submesh = submeshes[s]; + + // copy indices at their new positions + var indexStart = mesh.submeshIndexOffsets[submesh]; + var indexEnd = indexStart + mesh.submeshIndexCounts[submesh]; + for (var i = indexStart; i < indexEnd; i++) + { + result.indices[index] = indexOffset + i - indexStart; + result.vertices[index] = mesh.vertices[mesh.indices[i]]; + index++; + } + + // submesh data is mostly the same + result.submeshIndexCounts[s] = mesh.submeshIndexCounts[submesh]; + result.submeshMaterials[s] = mesh.submeshMaterials[submesh]; + result.submeshIndexOffsets[s] = indexOffset; + + // Update offset for next submesh + indexOffset += indexEnd - indexStart; + } + + return result; + } + } +} diff --git a/src/cs/vim/Vim.Format.Core/Geometry/VimShapeNext.cs b/src/cs/vim/Vim.Format.Core/Geometry/VimShapeNext.cs new file mode 100644 index 00000000..7c63bdd1 --- /dev/null +++ b/src/cs/vim/Vim.Format.Core/Geometry/VimShapeNext.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using Vim.G3d; +using Vim.Math3d; + +namespace Vim.Format.Geometry +{ + public class VimShapeNext + { + public readonly G3dVim g3d; + public readonly int Index; + public readonly ArraySegment Vertices; + + public Vector4 Color => g3d.ShapeColors[Index]; + public float Width => g3d.ShapeWidths[Index]; + + public static IEnumerable FromG3d(G3dVim g3d) + { + for(var i =0; i < g3d.GetShapeCount(); i++) + { + yield return new VimShapeNext(g3d, i); + } + } + + public VimShapeNext(G3dVim g3d, int index) + { + var start = g3d.GetShapeVertexStart(index); + var count = g3d.GetShapeVertexCount(index); + + Vertices = new ArraySegment(g3d.ShapeVertices, start, count); + } + } +} diff --git a/src/cs/vim/Vim.Format.Core/SerializableDocument.cs b/src/cs/vim/Vim.Format.Core/SerializableDocument.cs index 3a9ae487..6acaa935 100644 --- a/src/cs/vim/Vim.Format.Core/SerializableDocument.cs +++ b/src/cs/vim/Vim.Format.Core/SerializableDocument.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Text; using Vim.BFastLib; @@ -52,7 +51,7 @@ public class SerializableDocument /// /// The uninstanced / untransformed geometry /// - public G3d.G3D Geometry; + public G3dVim GeometryNext; /// /// The originating file name (if provided) @@ -91,9 +90,9 @@ public BFast ToBFast() bfast.SetArray(BufferNames.Strings, BFastStrings.Pack(StringTable)); } - if(Geometry != null) + if(GeometryNext != null) { - bfast.SetBFast(BufferNames.Geometry, Geometry?.ToBFast()); + bfast.SetBFast(BufferNames.Geometry, GeometryNext.ToBFast()); } return bfast; @@ -122,7 +121,7 @@ public static SerializableDocument FromBFast(BFast bfast, LoadOptions options = if (!doc.Options.SkipGeometry) { var geo = bfast.GetBFast(BufferNames.Geometry); - doc.Geometry = G3D.Read(geo); + doc.GeometryNext = new G3dVim(geo); } var entities = bfast.GetBFast(BufferNames.Entities); @@ -141,75 +140,10 @@ private static IEnumerable GetEntityTables( foreach (var entry in bfast.Entries) { var b = bfast.GetBFast(entry); - var table = ReadEntityTable(b, schemaOnly); + var table = SerializableEntityTable.FromBFast(b, schemaOnly); table.Name = entry; yield return table; } } - - /// - /// Returns a SerializableEntityTable based on the given buffer reader. - /// - public static SerializableEntityTable ReadEntityTable( - 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; - } } } diff --git a/src/cs/vim/Vim.Format.Core/SerializableEntityTable.cs b/src/cs/vim/Vim.Format.Core/SerializableEntityTable.cs index dd724bb9..d4211270 100644 --- a/src/cs/vim/Vim.Format.Core/SerializableEntityTable.cs +++ b/src/cs/vim/Vim.Format.Core/SerializableEntityTable.cs @@ -22,16 +22,45 @@ public class SerializableEntityTable /// Relation to another entity table. For example surface to element. /// public List> IndexColumns = new List>(); + public Dictionary> IndexColumnsMap = new Dictionary>(); /// /// Data encoded as strings in the global string table /// public List> StringColumns = new List>(); + public Dictionary> StringColumnsMap = new Dictionary>(); /// /// Numeric data encoded as byte, int, float, or doubles /// public List DataColumns = new List(); + public Dictionary DataColumnsMap = new Dictionary(); + + + public void BreakIndices(params string[] columns) + { + var set = new HashSet(columns); + IndexColumns = IndexColumns.Select(c => set.Contains(c.Name) ? c.Fill(VimConstants.NoEntityRelation) : c).ToList(); + IndexColumnsMap = IndexColumns.ToDictionary(c => c.Name, c => c); + } + + public void AddDataColumn(INamedBuffer buffer) + { + DataColumns.Add(buffer); + DataColumnsMap.Add(buffer.Name, buffer); + } + + public void AddIndexColumn(NamedBuffer buffer) + { + IndexColumns.Add(buffer); + IndexColumnsMap.Add(buffer.Name, buffer); + } + + public void AddStringColumn(NamedBuffer buffer) + { + StringColumns.Add(buffer); + StringColumnsMap.Add(buffer.Name, buffer); + } public IEnumerable ColumnNames => IndexColumns.Select(c => c.Name) @@ -43,11 +72,6 @@ public IEnumerable AllColumns .Concat(StringColumns) .Concat(DataColumns); - public static SerializableEntityTable FromBfast(string name, BFast bfast) - { - return null; - } - private readonly static Regex TypePrefixRegex = new Regex(@"(\w+:).*"); public static string GetTypeFromName(string name) @@ -56,12 +80,77 @@ public static string GetTypeFromName(string 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.AddIndexColumn(col.ToNamedBuffer(entry)); + break; + } + case VimConstants.StringColumnNameTypePrefix: + { + var col = schemaOnly ? new int[0] : bfast.GetArray(entry); + et.AddStringColumn(col.ToNamedBuffer(entry)); + break; + } + case VimConstants.IntColumnNameTypePrefix: + { + var col = schemaOnly ? new int[0] : bfast.GetArray(entry); + et.AddDataColumn(col.ToNamedBuffer(entry)); + break; + } + case VimConstants.LongColumnNameTypePrefix: + { + var col = schemaOnly ? new long[0] : bfast.GetArray(entry); + et.AddDataColumn(col.ToNamedBuffer(entry)); + break; + } + case VimConstants.DoubleColumnNameTypePrefix: + { + var col = schemaOnly ? new double[0] : bfast.GetArray(entry); + et.AddDataColumn(col.ToNamedBuffer(entry)); + break; + } + case VimConstants.FloatColumnNameTypePrefix: + { + var col = schemaOnly ? new float[0] : bfast.GetArray(entry); + et.AddDataColumn(col.ToNamedBuffer(entry)); + break; + } + case VimConstants.ByteColumnNameTypePrefix: + { + var col = schemaOnly ? new byte[0] : bfast.GetArray(entry); + et.AddDataColumn(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()); + bfast.SetArray(col.Name, col.ToBytes()); } return bfast; } diff --git a/src/cs/vim/Vim.Format.Core/SerializableHeaderExtensions.cs b/src/cs/vim/Vim.Format.Core/SerializableHeaderExtensions.cs index aea393b2..82a2537f 100644 --- a/src/cs/vim/Vim.Format.Core/SerializableHeaderExtensions.cs +++ b/src/cs/vim/Vim.Format.Core/SerializableHeaderExtensions.cs @@ -1,9 +1,15 @@ -using System.IO; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; namespace Vim.Format { public static class SerializableHeaderExtensions { + /// + /// Returns true if the SerializableHeader in the stream is successfully parsed. + /// public static bool TryParseSerializableHeader(this Stream stream, out SerializableHeader header) { try @@ -17,16 +23,23 @@ public static bool TryParseSerializableHeader(this Stream stream, out Serializab return header != null; } - public static bool TryParseSerializableHeader(this FileInfo fileInfo, out SerializableHeader header) + /// + /// Returns true if the SerializableHeader in the stream is successfully parsed. + /// + public static bool TryParseSerializableHeader(this FileInfo file, out SerializableHeader header) { - try + using (var stream = file.OpenRead()) { - header = SerializableHeader.FromPath(fileInfo.FullName); - } - catch - { - header = null; + try + { + header = SerializableHeader.FromStream(stream); + } + catch + { + header = null; + } } + return header != null; } diff --git a/src/cs/vim/Vim.Format.Core/Serializer.cs b/src/cs/vim/Vim.Format.Core/Serializer.cs deleted file mode 100644 index f89bb5f2..00000000 --- a/src/cs/vim/Vim.Format.Core/Serializer.cs +++ /dev/null @@ -1,250 +0,0 @@ -//using System.Collections.Generic; -//using System.Linq; -//using System; -//using Vim.BFastLib; -//using System.IO; -//using System.Text; -//using System.Text.RegularExpressions; -//using Vim.G3d; -//using Vim.Util; - -//namespace Vim.Format -//{ -// public static class Serializer -// { -// private static List ToBuffers(this SerializableEntityTable table) -// { -// var r = new List(); - -// r.AddRange(table.DataColumns); -// r.AddRange(table.IndexColumns); -// r.AddRange(table.StringColumns); - -// return r; -// } - -// private 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(); - -// /// -// /// Enumerates the SerializableEntityTables contained in the given entities buffer. -// /// -// public static IEnumerable EnumerateEntityTables( -// this BFastBufferReader entitiesBufferReader, -// bool schemaOnly, -// Func entityTableNameFilterFunc = null) -// { -// var entityTableBufferReaders = entitiesBufferReader.Seek() -// .GetBFastBufferReaders(br => entityTableNameFilterFunc?.Invoke(br.Name) ?? true); - -// foreach (var entityTableBufferReader in entityTableBufferReaders) -// { -// yield return entityTableBufferReader.ReadEntityTable(schemaOnly); -// } -// } - -// /// -// /// Enumerates the SerializableEntityTables contained in the given VIM file. -// /// -// public static IEnumerable EnumerateEntityTables( -// this FileInfo vimFileInfo, -// bool schemaOnly, -// Func entityTableNameFilterFunc = null) -// { -// using (var stream = vimFileInfo.OpenRead()) -// { -// var entitiesBufferReader = stream.GetBFastBufferReader(BufferNames.Entities); -// if (entitiesBufferReader == null) -// yield break; - -// foreach (var entityTable in entitiesBufferReader.EnumerateEntityTables(schemaOnly, entityTableNameFilterFunc)) -// { -// yield return entityTable; -// } -// } -// } - -// private static BFastBuilder ToBFastBuilder(this IEnumerable entityTables) -// { -// var bldr = new BFastBuilder(); -// foreach (var et in entityTables) -// { -// bldr.Add(et.Name, et.ToBuffers()); -// } -// return bldr; -// } - -// private static BFastBuilder ToBFastBuilder(this SerializableDocument doc) -// => CreateBFastBuilder( -// doc.Header, -// doc.Assets, -// doc.StringTable, -// doc.EntityTables, -// doc.Geometry.ToG3DWriter()); - -// private 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.FileName = filePath; -// return doc; -// } -// } -// } -//} diff --git a/src/cs/vim/Vim.Format.Core/TypePrefixExtensions.cs b/src/cs/vim/Vim.Format.Core/TypePrefixExtensions.cs deleted file mode 100644 index 43a991e8..00000000 --- a/src/cs/vim/Vim.Format.Core/TypePrefixExtensions.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.RegularExpressions; -using Vim.BFastLib; - -namespace Vim.Format -{ - public static class TypePrefixExtensions - { - private 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(); - } -} diff --git a/src/cs/vim/Vim.Format.Core/Validation.cs b/src/cs/vim/Vim.Format.Core/Validation.cs index 8e97c44d..d0d8b366 100644 --- a/src/cs/vim/Vim.Format.Core/Validation.cs +++ b/src/cs/vim/Vim.Format.Core/Validation.cs @@ -1,31 +1,27 @@ using System; -using System.Collections.Generic; -using System.Linq; using Vim.BFastLib; -using Vim.G3d; -using Vim.LinqArray; namespace Vim.Format { public static class Validation { - private static void ValidateTableRows(this Document doc) + public static void ValidateTableRows(this Document doc) { - foreach (var et in doc.EntityTables.Values.ToArray()) + foreach (var et in doc.Tables) { - foreach (var c in et.IndexColumns.Values.ToArray()) + foreach (var c in et.IndexColumns) { if (c.Array.Length != et.NumRows) throw new Exception($"Expected array length {c.Array.Length} of column {c.Name} to be the same as number of rows {et.NumRows}"); } - foreach (var c in et.DataColumns.Values.ToArray()) + foreach (var c in et.DataColumns) { if (c.NumElements() != et.NumRows) throw new Exception($"Expected array length {c.NumElements()} of column {c.Name} to be the same as number of rows {et.NumRows}"); } - foreach (var c in et.StringColumns.Values.ToArray()) + foreach (var c in et.StringColumns) { if (c.Array.Length != et.NumRows) throw new Exception($"Expected array length {c.Array.Length} of column {c.Name} to be the same as number of rows {et.NumRows}"); @@ -33,11 +29,11 @@ private static void ValidateTableRows(this Document doc) } } - private static void ValidateIndexColumns(this Document doc) + public static void ValidateIndexColumns(this Document doc) { - foreach (var et in doc.EntityTables.Values.ToArray()) + foreach (var et in doc.Tables) { - foreach (var ic in et.IndexColumns.Values) + foreach (var ic in et.IndexColumns) { var table = ic.GetRelatedTable(doc); if (table == null) @@ -46,35 +42,7 @@ private static void ValidateIndexColumns(this Document doc) } } - private static string[] RequiredAttributeNames => new [] - { - // Vertices - CommonAttributes.Position, - CommonAttributes.Index, - - // Meshes - CommonAttributes.MeshSubmeshOffset, - - // Submeshes - CommonAttributes.SubmeshIndexOffset, - - // Instances - CommonAttributes.InstanceMesh, - CommonAttributes.InstanceTransform, - }; - - private static void ValidateGeometryAttributes(this Document doc) - { - var attributes = doc.Geometry.Attributes; - var attributeNameSet = new HashSet(attributes.Select(a => a.Name)); - foreach (var attributeName in RequiredAttributeNames) - { - if (!attributeNameSet.Contains(attributeName)) - throw new Exception($"Required attribute {attributeName} was not found."); - } - } - - private static void ValidateAssets(this Document doc) + public static void ValidateAssets(this Document doc) { foreach (var asset in doc.Assets.Values) AssetInfo.Parse(asset.Name); // This will throw if it fails to parse. @@ -84,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 77e4bba7..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,5 +22,11 @@ + + + True + + + diff --git a/src/cs/vim/Vim.Format.Core/VimConstants.cs b/src/cs/vim/Vim.Format.Core/VimConstants.cs index 7b4c5e39..79dfed12 100644 --- a/src/cs/vim/Vim.Format.Core/VimConstants.cs +++ b/src/cs/vim/Vim.Format.Core/VimConstants.cs @@ -107,5 +107,15 @@ public static class VimConstants TableNames.Asset, TableNames.Material, }; + + public static class DisciplineNames + { + public const string Mechanical = nameof(Mechanical); + public const string Architecture = nameof(Architecture); + public const string Generic = nameof(Generic); + public const string Electrical = nameof(Electrical); + public const string Plumbing = nameof(Plumbing); + public const string Structural = nameof(Structural); + } } } diff --git a/src/cs/vim/Vim.Format.Core/VimSchema.cs b/src/cs/vim/Vim.Format.Core/VimSchema.cs index 29ff3a72..2ba40ebb 100644 --- a/src/cs/vim/Vim.Format.Core/VimSchema.cs +++ b/src/cs/vim/Vim.Format.Core/VimSchema.cs @@ -44,12 +44,15 @@ public EntityTableSchema AddEntityTableSchema(string entityTableName) } public static VimSchema Create(string filePath) - => Create(new Document(SerializableDocument.FromPath(filePath))); + => Create(SerializableDocument.FromPath(filePath, new LoadOptions() { SchemaOnly=true}) ); - public static VimSchema Create(Document doc) + public static VimSchema Create(SerializableDocument doc) + => Create(new Document(doc)); + + private static VimSchema Create(Document doc) { var vimSchema = new VimSchema(doc.Header); - foreach (var entityTable in doc.EntityTables.Values) + foreach (var entityTable in doc.Tables) { var ets = vimSchema.AddEntityTableSchema(entityTable.Name); diff --git a/src/cs/vim/Vim.Format.Tests/EntityTable_v2_Tests.cs b/src/cs/vim/Vim.Format.Tests/EntityTable_v2_Tests.cs index 96973c1e..c111ac94 100644 --- a/src/cs/vim/Vim.Format.Tests/EntityTable_v2_Tests.cs +++ b/src/cs/vim/Vim.Format.Tests/EntityTable_v2_Tests.cs @@ -2,7 +2,6 @@ using System.IO; using System.Linq; using Vim.Format.ObjectModel; -using Vim.LinqArray; using Vim.Util.Tests; namespace Vim.Format.Tests; diff --git a/src/cs/vim/Vim.Format.Tests/FormatTests.cs b/src/cs/vim/Vim.Format.Tests/FormatTests.cs index a9e5adf0..8fffe617 100644 --- a/src/cs/vim/Vim.Format.Tests/FormatTests.cs +++ b/src/cs/vim/Vim.Format.Tests/FormatTests.cs @@ -2,7 +2,6 @@ using System.Linq; using NUnit.Framework; using Vim.BFastLib; -using Vim.LinqArray; using Vim.Util; namespace Vim.Format.Tests @@ -69,9 +68,9 @@ public static void AssertEquals(SerializableDocument d1, SerializableDocument d2 public static void AssertEquals(EntityTable et1, EntityTable et2) { Assert.AreEqual(et1.Name, et2.Name); - Assert.AreEqual(et1.DataColumns.Keys.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()); + Assert.AreEqual(et1.DataColumnNames.OrderBy(n => n).ToArray(), et2.DataColumnNames.OrderBy(n => n).ToArray()); + Assert.AreEqual(et1.IndexColumnNames.OrderBy(n => n).ToArray(), et2.IndexColumnNames.OrderBy(n => n).ToArray()); + Assert.AreEqual(et1.StringColumnNames.OrderBy(n => n).ToArray(), et2.StringColumnNames.OrderBy(n => n).ToArray()); var columns1 = et1.Columns.OrderBy(c => c.Name).ToArray(); var columns2 = et2.Columns.OrderBy(c => c.Name).ToArray(); @@ -105,9 +104,9 @@ public static bool IsSupersetOf(IEnumerable a, IEnumerable b) public static void AssertIsSupersetOf(EntityTable et1, EntityTable et2) { Assert.AreEqual(et1.Name, et2.Name); - Assert.IsTrue(IsSupersetOf(et1.DataColumns.Keys, et2.DataColumns.Keys)); - Assert.IsTrue(IsSupersetOf(et1.IndexColumns.Keys, et2.IndexColumns.Keys)); - Assert.IsTrue(IsSupersetOf(et1.StringColumns.Keys, et2.StringColumns.Keys)); + Assert.IsTrue(IsSupersetOf(et1.DataColumnNames, et2.DataColumnNames)); + Assert.IsTrue(IsSupersetOf(et1.IndexColumnNames, et2.IndexColumnNames)); + Assert.IsTrue(IsSupersetOf(et1.StringColumnNames, et2.StringColumnNames)); var columns1 = et1.Columns.ToArray(); var columns2 = et2.Columns.ToArray(); @@ -132,12 +131,12 @@ public static void AssertIsSupersetOf(EntityTable et1, EntityTable et2) /// public static void AssertIsSuperSetOf(Document d1, Document d2, bool skipGeometryAndNodes = true) { - var schema1 = VimSchema.Create(d1); - var schema2 = VimSchema.Create(d2); + var schema1 = d1.GetSchema(); + var schema2 = d2.GetSchema(); Assert.IsTrue(VimSchema.IsSuperSetOf(schema1, schema2)); - var etKeys1 = d1.EntityTables.Keys; - var etKeys2 = d2.EntityTables.Keys; + var etKeys1 = d1.TableNames; + var etKeys2 = d2.TableNames; Assert.IsTrue(IsSupersetOf(etKeys1, etKeys2)); foreach (var key in etKeys2) @@ -145,8 +144,8 @@ public static void AssertIsSuperSetOf(Document d1, Document d2, bool skipGeometr if (skipGeometryAndNodes && key.ToLowerInvariant().Contains("geometry")) continue; - var et2 = d2.EntityTables[key]; - var et1 = d1.EntityTables.GetOrDefault(key); + var et2 = d2.GetTable(key); + var et1 = d1.GetTable(key); if (et1 == null) Assert.Fail($"No matching entity table found: {key}"); AssertIsSupersetOf(et1, et2); @@ -155,19 +154,19 @@ public static void AssertIsSuperSetOf(Document d1, Document d2, bool skipGeometr public static void AssertEquals(Document d1, Document d2, bool skipGeometryAndNodes = false) { - var schema1 = VimSchema.Create(d1); - var schema2 = VimSchema.Create(d2); + var schema1 = d1.GetSchema(); + var schema2 = d2.GetSchema(); Assert.IsTrue(VimSchema.IsSame(schema1, schema2)); - var entityTables1 = d1.EntityTables.Keys.OrderBy(n => n).ToArray(); - var entityTables2 = d2.EntityTables.Keys.OrderBy(n => n).ToArray(); + var entityTables1 = d1.TableNames.OrderBy(n => n).ToArray(); + var entityTables2 = d2.TableNames.OrderBy(n => n).ToArray(); Assert.AreEqual(entityTables1, entityTables2); foreach (var k in entityTables1) { if (skipGeometryAndNodes && k.ToLowerInvariant().Contains("geometry")) continue; - AssertEquals(d1.EntityTables[k], d2.EntityTables[k]); + AssertEquals(d1.GetTable(k), d2.GetTable(k)); } } } diff --git a/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs b/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs index b9828734..003cd78e 100644 --- a/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs +++ b/src/cs/vim/Vim.Format.Tests/Geometry/GeometryTests.cs @@ -1,6 +1,6 @@ -using System; +using NUnit.Framework; +using System; using System.Linq; -using NUnit.Framework; using Vim.Format.Geometry; using Vim.G3d; using Vim.Math3d; @@ -9,164 +9,119 @@ 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) }.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.TriMesh(TestTetrahedronIndices); - - //public static IMesh Torus = Primitives.Torus(10, 0.2f, 10, 24); - - //static IMesh RevolvedVerticalCylinder(float height, float radius, int verticalSegments, int radialSegments) - // => (Vector3.UnitZ * height).ToLine().Interpolate(verticalSegments).Add(-radius.AlongX()).RevolveAroundAxis(Vector3.UnitZ, radialSegments); - - //public static IMesh Cylinder = RevolvedVerticalCylinder(5, 1, 4, 12); - - public static IMesh[] AllMeshes = { + public static VimMesh XYTriangle = new VimMesh( + new[] { 0, 1, 2 }, new[] { + new Vector3(0f, 0f, 0f), + new Vector3(0f, 1f, 0f), + new Vector3(1f, 0f, 0f) + }); + + public static VimMesh XYQuad = VimMesh.FromQuad( + new int[] { 0, 1, 2, 3 }, new[]{ + new Vector3(0f, 0f, 0f), + new Vector3(0f, 1f, 0f), + new Vector3(1f, 1f, 0f), + new Vector3(1f, 0f, 0f) + }); + + public static VimMesh XYQuadFromFunc = Primitives.QuadMesh(uv => uv.ToVector3(), 1, 1); + public static VimMesh XYQuad2x2 = Primitives.QuadMesh(uv => uv.ToVector3(), 2, 2); + public static VimMesh XYTriangleTwice = XYTriangle.Merge2(XYTriangle.Translate(new Vector3(1, 0, 0))); + + public static VimMesh Tetrahedron = new VimMesh( + new[] { 0, 1, 2, 0, 3, 1, 1, 3, 2, 2, 3, 0 }, new[] { + Vector3.Zero, + Vector3.UnitX, + Vector3.UnitY, + Vector3.UnitZ + }); + + public static VimMesh Torus = Primitives.Torus(10, 0.2f, 10, 24); + + public static VimMesh[] AllMeshes = { XYTriangle, // 0 - //XYQuad, // 1 - //XYQuadFromFunc, // 2 - //XYQuad2x2, // 3 + XYQuad, // 1 + XYQuadFromFunc, // 2 + XYQuad2x2, // 3 Tetrahedron, // 4 - //Torus, // 5 - //Cylinder, // 6 - //XYTriangleTwice, // 7 + Torus, // 5 + XYTriangleTwice, // 7 }; public static double SmallTolerance = 0.0001; - public static void AssertEquals(IMesh g1, IMesh g2) + public static void AssertEquals(VimMesh g1, VimMesh g2) { Assert.AreEqual(g1.NumFaces, g2.NumFaces); Assert.AreEqual(g1.NumCorners, g2.NumCorners); Assert.AreEqual(g1.NumVertices, g2.NumVertices); - for (var i = 0; i < g1.Indices.Count; i++) + for (var i = 0; i < g1.indices.Length; i++) { - var v1 = g1.Vertices[g1.Indices[i]]; - var v2 = g2.Vertices[g2.Indices[i]]; + var v1 = g1.vertices[g1.indices[i]]; + var v2 = g2.vertices[g2.indices[i]]; Assert.IsTrue(v1.AlmostEquals(v2, (float)SmallTolerance)); } } - public static void AssertEqualsWithAttributes(IMesh g1, IMesh g2) + public static void GeometryNullOps(VimMesh g) { - 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}"); + g.GeometryEquals(g); + g.Translate(Vector3.Zero).GeometryEquals(g); + g.Scale(Vector3.Zero).GeometryEquals(g); + g.Transform(Matrix4x4.Identity).GeometryEquals(g); } - public static void OutputTriangleStatsSummary(IMesh g) + [Test] + public static void BasicTests() { - var triangles = g.Triangles(); - for (var i = 0; i < Math.Min(3, triangles.Count); ++i) + var nMesh = 0; + foreach (var g in AllMeshes) { - Console.WriteLine($"Triangle {i}"); - OutputTriangleStats(triangles[i]); + Console.WriteLine($"Testing mesh {nMesh++}"); + g.Validate(); + //ValidateGeometry(g.ToTriMesh()); } - if (triangles.Count > 3) - { - Console.WriteLine("..."); - Console.WriteLine($"Triangle {triangles.Count - 1}"); - OutputTriangleStats(triangles.Last()); - } + 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()); + Assert.AreEqual(new[] { 0, 1, 2 }, XYTriangle.indices.Length); + + Assert.AreEqual(3, XYQuad.NumCornersPerFace); + Assert.AreEqual(2, XYQuad.NumFaces); + Assert.AreEqual(4, XYQuad.vertices.Length); + Assert.AreEqual(6, XYQuad.indices.Length); + + Assert.IsTrue(XYQuad.Planar()); + Assert.AreEqual(new[] { 0, 1, 2, 0, 2, 3 }, XYQuad.indices.ToArray()); + + Assert.AreEqual(3, XYQuadFromFunc.NumCornersPerFace); + Assert.AreEqual(2, XYQuadFromFunc.NumFaces); + Assert.AreEqual(4, XYQuadFromFunc.vertices.Length); + Assert.AreEqual(6, XYQuadFromFunc.indices.Length); + + Assert.AreEqual(3, XYQuad2x2.NumCornersPerFace); + Assert.AreEqual(8, XYQuad2x2.NumFaces); + Assert.AreEqual(9, XYQuad2x2.vertices.Length); + Assert.AreEqual(24, XYQuad2x2.indices.Length); + + Assert.AreEqual(3, Tetrahedron.NumCornersPerFace); + Assert.AreEqual(4, Tetrahedron.NumFaces); + Assert.AreEqual(4, Tetrahedron.vertices.Length); + Assert.AreEqual(12, Tetrahedron.indices.Length); + + Assert.AreEqual(3, XYTriangleTwice.NumCornersPerFace); + Assert.AreEqual(2, XYTriangleTwice.NumFaces); + Assert.AreEqual(6, XYTriangleTwice.vertices.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); } - //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() { @@ -181,16 +136,16 @@ public static void OutputGeometryData() foreach (var g in AllMeshes) { Console.WriteLine($"Geometry {n++}"); - for (var i = 0; i < g.Vertices.Count && i < 10; ++i) + for (var i = 0; i < g.vertices.Length && i < 10; ++i) { - Console.WriteLine($"Vertex {i} {g.Vertices[i]}"); + Console.WriteLine($"Vertex {i} {g.vertices[i]}"); } - if (g.Vertices.Count > 10) + if (g.vertices.Length > 10) { - var last = g.Vertices.Count - 1; + var last = g.vertices.Length - 1; Console.WriteLine("..."); - Console.WriteLine($"Vertex {last} {g.Vertices[last]}"); + Console.WriteLine($"Vertex {last} {g.vertices[last]}"); } for (var i = 0; i < g.NumFaces && i < 10; ++i) @@ -198,7 +153,7 @@ public static void OutputGeometryData() Console.WriteLine($"Face {i}: {g.Triangle(i)}"); } - if (g.Vertices.Count > 10) + if (g.vertices.Length > 10) { var last = g.NumFaces - 1; Console.WriteLine("..."); @@ -211,22 +166,22 @@ public static void OutputGeometryData() public static void StripIndicesTests() { var emptyStrip00 = Primitives.QuadMeshStripIndicesFromPointRows(0, 0); - Assert.AreEqual(0, emptyStrip00.Count); + Assert.AreEqual(0, emptyStrip00.Length); var emptyStrip01 = Primitives.QuadMeshStripIndicesFromPointRows(0, 1); - Assert.AreEqual(0, emptyStrip01.Count); + Assert.AreEqual(0, emptyStrip01.Length); var emptyStrip10 = Primitives.QuadMeshStripIndicesFromPointRows(1, 0); - Assert.AreEqual(0, emptyStrip10.Count); + Assert.AreEqual(0, emptyStrip10.Length); var emptyStrip11 = Primitives.QuadMeshStripIndicesFromPointRows(1, 1); - Assert.AreEqual(0, emptyStrip11.Count); + Assert.AreEqual(0, emptyStrip11.Length); var emptyStrip12 = Primitives.QuadMeshStripIndicesFromPointRows(1, 2); - Assert.AreEqual(0, emptyStrip12.Count); + Assert.AreEqual(0, emptyStrip12.Length); var emptyStrip21 = Primitives.QuadMeshStripIndicesFromPointRows(2, 1); - Assert.AreEqual(0, emptyStrip21.Count); + Assert.AreEqual(0, emptyStrip21.Length); // COUNTER-CLOCKWISE TEST (DEFAULT) // 2------3 <--- row 1: [2,3] @@ -234,7 +189,7 @@ public static void StripIndicesTests() // | | // 0------1 <--- row 0: [0,1] var strip22 = Primitives.QuadMeshStripIndicesFromPointRows(2, 2); - Assert.AreEqual(4, strip22.Count); + Assert.AreEqual(4, strip22.Length); Assert.AreEqual(0, strip22[0]); Assert.AreEqual(1, strip22[1]); Assert.AreEqual(3, strip22[2]); @@ -246,14 +201,15 @@ public static void StripIndicesTests() // | | // 0------1 <--- row 0: [0,1] var clockwiseStrip22 = Primitives.QuadMeshStripIndicesFromPointRows(2, 2, true); - Assert.AreEqual(4, clockwiseStrip22.Count); + Assert.AreEqual(4, clockwiseStrip22.Length); Assert.AreEqual(2, clockwiseStrip22[0]); Assert.AreEqual(3, clockwiseStrip22[1]); Assert.AreEqual(1, clockwiseStrip22[2]); Assert.AreEqual(0, clockwiseStrip22[3]); - for (var i = 0; i < strip22.Count; ++i) + var reversed22 = clockwiseStrip22.Reverse().ToArray(); + for (var i = 0; i < strip22.Length; ++i) { - Assert.AreEqual(strip22[i], clockwiseStrip22[^i]); + Assert.AreEqual(strip22[i], reversed22[i]); } // *------*------* @@ -261,7 +217,7 @@ public static void StripIndicesTests() // | | | // *------*------* var strip23 = Primitives.QuadMeshStripIndicesFromPointRows(2, 3); - Assert.AreEqual(4 * 2, strip23.Count); + Assert.AreEqual(4 * 2, strip23.Length); // *------*------*------* // | | | | @@ -271,46 +227,36 @@ public static void StripIndicesTests() // | | | | // *------*------*------* var strip34 = Primitives.QuadMeshStripIndicesFromPointRows(3, 4); - Assert.AreEqual(4 * 6, strip34.Count); + Assert.AreEqual(4 * 6, strip34.Length); } [Test] public static void TriangleSerializationTest() { - // Serialize a triangle g3d to a bfast as bytes and read it back. - var vertices = new[] + //TODO: Check the need for this test. + // Serializing a triangle is that a use case ? + + var mesh = new VimMesh(new[] { 0, 1, 2 }, new[] { new Vector3(0, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 1) - }; - - var indices = new[] { 0, 1, 2 }; - var submeshIndexOffsets = new[] { 0 }; - var submeshMaterials = new[] { 0 }; - - var g3d = new G3DBuilder() - .AddVertices(vertices) - .AddIndices(indices) - .Add(submeshIndexOffsets.ToSubmeshIndexOffsetAttribute()) - .Add(submeshMaterials.ToSubmeshMaterialAttribute()) - .ToG3D(); - - var bfast = g3d.ToBFast(); - var readG3d = G3D.Read(bfast); - - 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.Single()); - Assert.AreEqual(0, mesh.SubmeshMaterials.Single()); - Assert.AreEqual(0, mesh.GetFaceMaterials().First()); + }); + + var bfast = mesh.ToG3d().ToBFast(); + var result = VimMesh.FromG3d(new G3dVim(bfast)); + + Assert.IsNotNull(mesh); + result.Validate(); + + Assert.AreEqual(3, result.NumVertices); + Assert.AreEqual(new Vector3(0, 0, 0), result.vertices[0]); + Assert.AreEqual(new Vector3(0, 1, 0), result.vertices[1]); + Assert.AreEqual(new Vector3(0, 1, 1), result.vertices[2]); + Assert.AreEqual(1, result.NumFaces); + Assert.AreEqual(0, result.submeshIndexOffsets.Single()); + Assert.AreEqual(-1, result.submeshMaterials.Single()); + Assert.AreEqual(-1, result.GetFaceMaterials().First()); } } } diff --git a/src/cs/vim/Vim.Format.Tests/Geometry/PerimeterTest.cs b/src/cs/vim/Vim.Format.Tests/Geometry/PerimeterTest.cs new file mode 100644 index 00000000..82f2ac6c --- /dev/null +++ b/src/cs/vim/Vim.Format.Tests/Geometry/PerimeterTest.cs @@ -0,0 +1,27 @@ +using NUnit.Framework; +using Vim.Format.Geometry; +using Vim.Math3d; + +namespace Vim.Format.Tests.Geometry +{ + public static class PerimeterTest + { + + [Test] + public static void Test() + { + var torus = Primitives.QuadMesh(uv => TorusFunction(uv, 10, 0.2f), 10, 24); + + var perimeter = torus.GeneratePerimeter(Vector3.UnitX); + } + + public static Vector3 TorusFunction(Vector2 uv, float radius, float tube) + { + uv = uv * Constants.TwoPi; + return new Vector3( + (radius + tube * uv.Y.Cos()) * uv.X.Cos(), + (radius + tube * uv.Y.Cos()) * uv.X.Sin(), + tube * uv.X.Sin()); + } + } +} diff --git a/src/cs/vim/Vim.Format.Tests/SerializableDocumentTests.cs b/src/cs/vim/Vim.Format.Tests/SerializableDocumentTests.cs new file mode 100644 index 00000000..392fd04b --- /dev/null +++ b/src/cs/vim/Vim.Format.Tests/SerializableDocumentTests.cs @@ -0,0 +1,174 @@ +using NUnit.Framework; +using Vim.Format.Geometry; +using Vim.Format.SceneBuilder; +using Vim.Util.Tests; + +namespace Vim.Format.Tests; + + + +[TestFixture] +public static class SerializableDocumentTests +{ + [Test] + public static void TestEmpty() + { + var doc = new SerializableDocument(); + Assert.DoesNotThrow(() => doc.ToBFast()); + } + + [Test] + public static void CanOpenVim() + { + var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + var scene = VimScene.LoadVim(path); + scene.Validate(); + } + + //[Test] + //public static void GetMesh_IsSameMesh() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + // for (var i = 0; i < scene.GetMeshCount(); i++) + // { + // var mesh = scene.MeshesOld[i]; + // var next = scene.MeshesNext[i]; + // var raw = scene.Meshes[i]; + // MeshesAreSame(mesh, next); + // MeshesAreSame(mesh, raw); + // } + //} + + //[Test] + //public static void GetMesh_Transform_IsSame() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + // for (var i = 0; i < scene.GetMeshCount(); i++) + // { + // var mat = Matrix4x4.CreateWorld( + // new Vector3(1, -2, 3), + // new Vector3(0, 0, 1), + // new Vector3(0, 1, 0) + // ); + // var mesh = scene.MeshesOld[i].Transform(mat); + // var next = scene.MeshesNext[i].Transform(mat); + // var raw = scene.Meshes[i].Transform(mat); + // MeshesAreSame(mesh, next); + // MeshesAreSame(mesh, raw); + // } + //} + + //[Test] + //public static void ReverseWindingOrder_IsSame() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + // for (var i = 0; i < scene.GetMeshCount(); i++) + // { + + // var mesh = scene.MeshesOld[i].ReverseWindingOrder() as VimMesh; + // var next = scene.MeshesNext[i].ReverseWindingOrder(); + // var raw = scene.Meshes[i].ReverseWindingOrder(); + // MeshesAreSame(mesh, next); + // MeshesAreSame(mesh, raw); + // } + //} + + //[Test] + //public static void FaceMaterial_IsSame() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + // for (var i = 0; i < scene.GetMeshCount(); i++) + // { + // var mesh = scene.MeshesOld[i].GetFaceMaterials(); + // var next = scene.MeshesNext[i].GetFaceMaterials(); + // var raw = scene.Meshes[i].GetFaceMaterials(); + // Assert.That(mesh.SequenceEquals(next)); + // Assert.That(next.SequenceEquals(raw)); + // } + //} + + //[Test] + //public static void Merge_ByPair_IsSame() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + // for (var i = 1; i < scene.GetMeshCount(); i++) + // { + // var mesh = scene.MeshesOld[i].Merge(scene.MeshesOld[i - 1]); + // var next = scene.MeshesNext[i].Merge2(scene.MeshesNext[i - 1]); + // var raw = scene.Meshes[i].Merge2(scene.Meshes[i - 1]); + // MeshesAreSame(mesh, next); + // MeshesAreSame(mesh, raw); + // } + //} + + //[Test] + //public static void FromG3d_Equals_ToVimMesh() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + // var mesh = scene.Document.Geometry as VimMesh; + // var next = VimMesh.FromG3d(scene.Document.GeometryNext); + // MeshesAreSame(mesh, next); + + //} + + //[Test] + //public static void Merge_All_IsSame() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + + // var mesh = scene.MeshesOld[0].Merge(scene.MeshesOld.Skip(1).ToArray()); + // var next = scene.MeshesNext[0].Merge2(scene.MeshesNext.Skip(1).ToArray()); + // var raw = scene.Meshes[0].Merge2(scene.Meshes.Skip(1).ToArray()); + // MeshesAreSame(mesh, next); + // MeshesAreSame(mesh, raw); + //} + + //[Test] + //public static void SplitByMaterials_IsSame() + //{ + // var path = VimFormatRepoPaths.GetLatestWolfordResidenceVim(); + // var scene = VimScene.LoadVim(path); + // for (var i = 0; i < scene.GetMeshCount(); i++) + // { + // var mesh = scene.MeshesOld[i].SplitByMaterial().ToArray(); + // var next = scene.MeshesNext[i].SplitByMaterial(); + // var raw = scene.Meshes[i].SplitByMaterial(); + // Assert.AreEqual(mesh.Length, next.Length); + // Assert.AreEqual(mesh.Length, raw.Length); + + // for (var j = 0; j < mesh.Length; j++) + // { + // MeshesAreSame(mesh[j].Mesh, next[j].mesh); + // MeshesAreSame(mesh[j].Mesh, raw[j].mesh); + // } + // } + //} + + //private static void Consume(VimMesh mesh) + //{ + // mesh.Indices.Sum(); + // mesh.Vertices.Sum(v => v.X); + // mesh.SubmeshIndexOffsets.Sum(); + // mesh.SubmeshIndexCount.Sum(); + // mesh.SubmeshMaterials.Sum(); + //} + + private static void MaterialsAreSame(IMaterial mesh, VimMaterialNext next) + { + Assert.AreEqual(mesh.Color, next.Color); + Assert.AreEqual(mesh.Glossiness, next.Glossiness); + Assert.AreEqual(mesh.Smoothness, next.Smoothness); + } + + + + +} + diff --git a/src/cs/vim/Vim.Format.Tests/TrainingSetTests.cs b/src/cs/vim/Vim.Format.Tests/TrainingSetTests.cs index 55981164..f4bbf810 100644 --- a/src/cs/vim/Vim.Format.Tests/TrainingSetTests.cs +++ b/src/cs/vim/Vim.Format.Tests/TrainingSetTests.cs @@ -5,7 +5,6 @@ using System.Globalization; using System.IO; using System.Linq; -using Vim.LinqArray; using Vim.Format.ObjectModel; using Vim.Util; using Vim.Util.Tests; diff --git a/src/cs/vim/Vim.Format.Tests/Vim.Format.Tests.csproj b/src/cs/vim/Vim.Format.Tests/Vim.Format.Tests.csproj index d26c5621..afad46c3 100644 --- a/src/cs/vim/Vim.Format.Tests/Vim.Format.Tests.csproj +++ b/src/cs/vim/Vim.Format.Tests/Vim.Format.Tests.csproj @@ -17,9 +17,14 @@ - - - + + + + + + True + + diff --git a/src/cs/vim/Vim.Format.Vimx.Conversion/Chunking.cs b/src/cs/vim/Vim.Format.Vimx.Conversion/Chunking.cs new file mode 100644 index 00000000..8ddf6187 --- /dev/null +++ b/src/cs/vim/Vim.Format.Vimx.Conversion/Chunking.cs @@ -0,0 +1,198 @@ +using System.Collections.Generic; +using System.Linq; +using Vim.Math3d; +using Vim.G3d; + +namespace Vim.Format.VimxLib.Conversion +{ + public static class Chunking + { + public static VimChunks CreateChunks(ChunksDescription description) + { + var chunks = new G3dChunk[description.ChunkMeshes.Count]; + for (var i = 0; i < chunks.Length; i++) + { + var meshes = description.ChunkMeshes[i]; + chunks[i] = CreateChunk(description.g3d, meshes); + } + return new VimChunks(description, chunks); + } + + public static ChunksDescription ComputeChunks(MeshOrder ordering) + { + // 2MB once compressed -> 0.5MB + const int ChunkSize = 2000000; + + var meshChunks = new int[ordering.Meshes.Length]; + var meshIndex = new int[ordering.Meshes.Length]; + + var chunks = new List>(); + var chunk = new List(); + var chunkSize = 0L; + for (var i = 0; i < ordering.Meshes.Length; i++) + { + var mesh = ordering.Meshes[i]; + chunkSize += ordering.g3d.GetApproxSize(mesh); + if (chunkSize > ChunkSize && chunk.Count > 0) + { + chunks.Add(chunk); + chunk = new List(); + chunkSize = 0; + } + + meshChunks[i] = chunks.Count; + meshIndex[i] = chunk.Count; + chunk.Add(mesh); + } + if (chunk.Count > 0) + { + chunks.Add(chunk); + } + + return new ChunksDescription(ordering, meshChunks, meshIndex, chunks); + } + + public class SubmeshBuffer + { + int index = 0; + public int[] IndexOffsets; + public int[] VertexOffsets; + public int[] Materials; + + public SubmeshBuffer(int count) + { + IndexOffsets = new int[count]; + VertexOffsets = new int[count]; + Materials = new int[count]; + } + + public void Add(int indexOffset, int vertexOffset, int material) + { + IndexOffsets[index] = indexOffset; + VertexOffsets[index] = vertexOffset; + Materials[index] = material; + index++; + } + } + + public class PointsBuffer + { + public int[] indices; + public List vertices; + + public int IndexCount { get; private set; } = 0; + public int VertexCount => vertices.Count; + + public PointsBuffer(int indexCount, int vertexCount) + { + indices = new int[indexCount]; + vertices = new List(vertexCount); + } + + public void AddIndex(int index) + { + indices[IndexCount++] = index; + } + + public void AddVertex(Vector3 vertex) + { + vertices.Add(vertex); + } + } + + public static G3dChunk CreateChunk(G3dVim g3d, List meshes) + { + var meshSubmeshOffsets = new int[meshes.Count + 1]; + var meshOpaqueCounts = new int[meshes.Count]; + + var submeshCount = meshes.Sum(m => g3d.GetMeshSubmeshCount(m)); + var submeshBuffer = new SubmeshBuffer(submeshCount); + + var indexCount = meshes.Sum(m => g3d.GetMeshIndexCount(m)); + var vertexCount = meshes.Sum(m => g3d.GetMeshVertexCount(m)); + var pointsBuffer = new PointsBuffer(indexCount, vertexCount); + + for (var i = 0; i < meshes.Count; i++) + { + var mesh = meshes[i]; + + var opaqueCount = AppendSubmeshes( + g3d, + mesh, + false, + submeshBuffer, + pointsBuffer + ); + + var transparentCount = AppendSubmeshes( + g3d, + mesh, + true, + submeshBuffer, + pointsBuffer + ); + meshOpaqueCounts[i] = opaqueCount; + meshSubmeshOffsets[i + 1] = meshSubmeshOffsets[i] + opaqueCount + transparentCount; + } + + return new G3dChunk( + meshSubmeshOffset: meshSubmeshOffsets, + meshOpaqueSubmeshCounts: meshOpaqueCounts, + submeshIndexOffsets: submeshBuffer.IndexOffsets, + submeshVertexOffsets :submeshBuffer.VertexOffsets, + submeshMaterials: submeshBuffer.Materials, + indices: pointsBuffer.indices, + positions: pointsBuffer.vertices.ToArray() + ); + } + + private static int AppendSubmeshes( + G3dVim g3d, + int mesh, + bool transparent, + SubmeshBuffer submeshBuffer, + PointsBuffer pointsBuffer + ) + { + var subStart = g3d.GetMeshSubmeshStart(mesh); + var subEnd = g3d.GetMeshSubmeshEnd(mesh); + var count = 0; + for (var sub = subStart; sub < subEnd; sub++) + { + var currentMat = g3d.SubmeshMaterials[sub]; + var color = currentMat > 0 ? g3d.MaterialColors[currentMat] : Vector4.One; + var accept = color.W < 1 == transparent; + + if (!accept) continue; + count++; + submeshBuffer.Add(pointsBuffer.IndexCount, pointsBuffer.VertexCount, currentMat); + g3d.GetSubmesh(sub, pointsBuffer); + } + return count; + } + + private static void GetSubmesh(this G3dVim g3d, int submesh, PointsBuffer points) + { + var index = points.VertexCount; + var dict = new Dictionary(); + var start = g3d.GetSubmeshIndexStart(submesh); + var end = g3d.GetSubmeshIndexEnd(submesh); + + for (var i = start; i < end; i++) + { + var v = g3d.Indices[i]; + if (dict.ContainsKey(v)) + { + points.AddIndex(dict[v]); + } + else + { + points.AddIndex(index); + points.AddVertex(g3d.Positions[v]); + dict.Add(v, index); + index++; + } + } + } + } +} diff --git a/src/cs/vim/Vim.Format.Vimx.Conversion/Ordering.cs b/src/cs/vim/Vim.Format.Vimx.Conversion/Ordering.cs new file mode 100644 index 00000000..587bac7e --- /dev/null +++ b/src/cs/vim/Vim.Format.Vimx.Conversion/Ordering.cs @@ -0,0 +1,78 @@ +using System; +using Vim.Format.ObjectModel; +using Vim.G3d; + +namespace Vim.Format.VimxLib.Conversion +{ + public static class Ordering + { + public static MeshOrder ComputeOrder(G3dVim g3d, DocumentModel bim) + { + var meshCount = g3d.GetMeshCount(); + var resultCount = 0; + for (var mesh = 0; mesh < meshCount; mesh++) + { + if (g3d.GetMeshInstances(mesh).Count > 0) resultCount++; + } + + var i = 0; + var order = new int[resultCount]; + var instanceCount = 0; + for (var mesh = 0; mesh < meshCount; mesh++) + { + var instances = g3d.GetMeshInstances(mesh); + if (instances.Count > 0) + { + instanceCount += instances.Count; + order[i++] = mesh; + } + } + Array.Sort(order, (a, b) => + { + var prioA = GetPriority(GetMeshName(g3d, bim, a)); + var prioB = GetPriority(GetMeshName(g3d, bim, b)); + return prioA - prioB; + }); + return new MeshOrder(g3d, order, instanceCount); + } + + static string GetMeshName(G3dVim g3d, DocumentModel bim, int mesh) + { + var node = g3d.GetMeshInstances(mesh)[0]; + + if (node < 0 || node >= bim.NodeElementIndex.Length) return ""; + var element = bim.NodeElementIndex[node]; + + if (element < 0 || element >= bim.ElementCategoryIndex.Length) return ""; + var category = bim.ElementCategoryIndex[element]; + + if (category < 0 || category >= bim.CategoryName.Length) return ""; + var name = bim.CategoryName[category]; + + return name; + } + + static int GetPriority(string value) + { + if (string.IsNullOrWhiteSpace(value)) return 0; + + if (value.Contains("Topography")) return 110; + if (value.Contains("Floor")) return 100; + if (value.Contains("Slab")) return 100; + if (value.Contains("Ceiling")) return 90; + if (value.Contains("Roof")) return 90; + + if (value.Contains("Curtain")) return 80; + if (value.Contains("Wall")) return 80; + if (value.Contains("Window")) return 70; + + if (value.Contains("Column")) return 60; + if (value.Contains("Structural")) return 60; + + if (value.Contains("Stair")) return 40; + if (value.Contains("Doors")) return 30; + + return 1; + } + } +} diff --git a/src/cs/vim/Vim.Format.Vimx.Conversion/Vim.Format.Vimx.Conversion.csproj b/src/cs/vim/Vim.Format.Vimx.Conversion/Vim.Format.Vimx.Conversion.csproj new file mode 100644 index 00000000..05a0161b --- /dev/null +++ b/src/cs/vim/Vim.Format.Vimx.Conversion/Vim.Format.Vimx.Conversion.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + + + + + + + + + + True + + + + + diff --git a/src/cs/vim/Vim.Format.Vimx.Conversion/VimxConverter.cs b/src/cs/vim/Vim.Format.Vimx.Conversion/VimxConverter.cs new file mode 100644 index 00000000..b233e655 --- /dev/null +++ b/src/cs/vim/Vim.Format.Vimx.Conversion/VimxConverter.cs @@ -0,0 +1,224 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Vim.Format.ObjectModel; +using Vim.G3d; +using Vim.Math3d; +using Vim.Util; + +namespace Vim.Format.VimxLib.Conversion +{ + public static class VimxConverter + { + public static Vimx FromVimPath(string vimPath) + { + var vim = VimScene.LoadVim(vimPath, new LoadOptions() + { + SkipAssets = true, + SkipGeometry = true, + }); + + var g3d = G3dVim.FromVim(vimPath); + var vimx = ConvertFromVim(g3d, vim.DocumentModel); + + return vimx; + } + + public static Vimx ConvertFromVim(G3dVim g3d, DocumentModel bim) + { + // Split input Vim into chunks. + var chunks = CreateChunks(g3d, bim); + + // Compute the scene definition from chunks. + var scene = CreateScene(chunks, bim); + + // Materials are reused from input g3d. + var materials = new G3dMaterials(g3d); + + var header = VimxHeader.CreateDefault(); + + return new Vimx(header, MetaHeader.Default, scene, materials, chunks.Chunks); + } + + public static VimChunks CreateChunks(G3dVim g3d, DocumentModel bim) + { + // First compute a desirable presentation order. + var ordering = Ordering.ComputeOrder(g3d, bim); + + // Groups meshes up to a certain size. + var groups = Chunking.ComputeChunks(ordering); + // Append and merge geometry from g3d to create the chunks. + + var chunks = Chunking.CreateChunks(groups); + + return chunks; + } + + public static G3dScene CreateScene(VimChunks chunks, DocumentModel bim) + { + var nodeElements = bim.NodeElementIndex.ToArray(); + + var instanceCount = chunks.InstanceCount; + var instanceNodes = new int[instanceCount]; + var instanceMeshes = new int[instanceCount]; + var instanceGroups = new int[instanceCount]; + var instanceTransforms = new Matrix4x4[instanceCount]; + var instanceFlags = new ushort[instanceCount]; + var instanceTags = new long[instanceCount]; + var instanceMins = new Vector3[instanceCount]; + var instanceMaxs = new Vector3[instanceCount]; + + var meshCount = chunks.MeshCount; + var indexCounts = new int[meshCount]; + var vertexCounts = new int[meshCount]; + var opaqueIndexCounts = new int[meshCount]; + var opaqueVertexCounts = new int[meshCount]; + + var sw = Stopwatch.StartNew(); + sw.Stop(); + var instance = 0; + for (var i = 0; i < meshCount; i++) + { + var meshChunk = chunks.MeshChunks[i]; + var meshIndex = chunks.MeshIndex[i]; + var instances = chunks.GetInstances(i); + var chunk = chunks.Chunks[meshChunk]; + for (var j = 0; j < instances.Count; j++) + { + var node = instances[j]; + var element = nodeElements[node]; + var transform = chunks.g3d.InstanceTransforms[node]; + + // geometry + instanceMeshes[instance] = i; + instanceTransforms[instance] = transform; + + // bounding box + sw.Start(); + var box = chunk.GetAABox(meshIndex, transform); + sw.Stop(); + instanceMins[instance] = box.Min; + instanceMaxs[instance] = box.Max; + + // bim + instanceNodes[instance] = node; + instanceGroups[instance] = element; + instanceTags[instance] = bim.ElementId.ElementAtOrDefault(element, -1); + + instance++; + } + + // geometry counts + indexCounts[i] = chunk.GetMeshIndexCount(meshIndex, MeshSection.All); + vertexCounts[i] = chunk.GetMeshVertexCount(meshIndex, MeshSection.All); + opaqueIndexCounts[i] = chunk.GetMeshIndexCount(meshIndex, MeshSection.Opaque); + opaqueVertexCounts[i] = chunk.GetMeshVertexCount(meshIndex, MeshSection.Opaque); ; + } + + // InstanceFlags is not always present. + if (chunks.g3d.InstanceFlags != null) + { + for(var i = 0; i < instanceNodes.Length; i++) + { + var node = instanceNodes[i]; + instanceFlags[i] = chunks.g3d.InstanceFlags[node]; + } + } + + var scene = new G3dScene( + + chunkCount: new[] { chunks.ChunkCount}, + instanceMeshes : instanceMeshes, + instanceTransformData: instanceTransforms, + instanceNodes: instanceNodes, + instanceFlags: instanceFlags, + instanceGroups: instanceGroups, + instanceMaxs: instanceMaxs, + instanceMins: instanceMins, + instanceTags: instanceTags, + meshChunks: chunks.MeshChunks, + meshChunkIndices: chunks.MeshIndex, + meshIndexCounts : indexCounts, + meshVertexCounts: vertexCounts, + meshOpaqueIndexCounts: opaqueIndexCounts, + meshOpaqueVertexCounts: opaqueVertexCounts + ); + return scene; + } + } + + /// + /// Initial step of vim->vimx conversion. + /// + public class MeshOrder + { + public readonly G3dVim g3d; + public readonly int[] Meshes; + public readonly int InstanceCount; + + public MeshOrder(G3dVim g3d, int[] meshes, int instanceCount) + { + this.g3d = g3d; + Meshes = meshes; + InstanceCount = instanceCount; + } + } + + /// + /// Describes how the meshes from the vim will be grouped in the vimx. + /// + public class ChunksDescription + { + public readonly G3dVim g3d; + public readonly int[] Meshes; + public readonly int[] MeshChunks; + public readonly int[] MeshIndex; + public readonly List> ChunkMeshes; + public readonly int InstanceCount; + + public ChunksDescription(MeshOrder ordering, int[] meshChunks, int[] meshIndex, List> chunkMeshes) + { + g3d = ordering.g3d; + Meshes = ordering.Meshes; + InstanceCount = ordering.InstanceCount; + MeshChunks = meshChunks; + MeshIndex = meshIndex; + ChunkMeshes = chunkMeshes; + } + } + + /// + /// Resulting Chunks of the vim->vimx conversion. + /// + public class VimChunks + { + public readonly G3dVim g3d; + public readonly int[] Meshes; + public readonly int[] MeshChunks; + public readonly int[] MeshIndex; + public readonly List> ChunkMeshes; + public readonly G3dChunk[] Chunks; + public readonly int InstanceCount; + + public VimChunks(ChunksDescription description, G3dChunk[] chunks) + { + g3d = description.g3d; + Meshes = description.Meshes; + MeshChunks = description.MeshChunks; + MeshIndex = description.MeshIndex; + ChunkMeshes = description.ChunkMeshes; + InstanceCount = description.InstanceCount; + Chunks = chunks; + } + + public int ChunkCount => Chunks.Length; + + public int MeshCount => Meshes.Length; + + public IReadOnlyList GetInstances(int meshIndex) + { + var m = Meshes[meshIndex]; + return g3d.GetMeshInstances(m); + } + } +} diff --git a/src/cs/g3d/Vim.G3dNext/Vim.G3dNext.csproj b/src/cs/vim/Vim.Format.Vimx/Vim.Format.Vimx.csproj similarity index 65% rename from src/cs/g3d/Vim.G3dNext/Vim.G3dNext.csproj rename to src/cs/vim/Vim.Format.Vimx/Vim.Format.Vimx.csproj index b0086596..b9896ad8 100644 --- a/src/cs/g3d/Vim.G3dNext/Vim.G3dNext.csproj +++ b/src/cs/vim/Vim.Format.Vimx/Vim.Format.Vimx.csproj @@ -1,16 +1,17 @@  + - netstandard2.0 + netstandard2.0 - - + + 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/Merge/MergeService.cs b/src/cs/vim/Vim.Format/Merge/MergeService.cs index e8dbbfc3..337859e0 100644 --- a/src/cs/vim/Vim.Format/Merge/MergeService.cs +++ b/src/cs/vim/Vim.Format/Merge/MergeService.cs @@ -134,18 +134,18 @@ public static DocumentBuilder MergeVimScenes( progress?.Report("Merging geometry"); ct.ThrowIfCancellationRequested(); - var materialCounts = vims.Select(v => v.Materials.Count).ToArray(); + var materialCounts = vims.Select(v => v.Materials.Length).ToArray(); var materialOffsets = materialCounts.PostAccumulate((x, y) => x + y).DropLast(); db.Geometry.AddMeshes(vims .SelectMany((vim, vimIndex) => vim.Meshes.Select(mesh => (mesh, vimIndex))) .Select( - pair => new DocumentBuilder.SubdividedMesh( - indices: pair.mesh.Indices?.ToList(), - vertices: pair.mesh.Vertices?.ToList(), - submeshesIndexOffset: pair.mesh.SubmeshIndexOffsets?.ToList(), - submeshMaterials: pair.mesh.SubmeshMaterials.Select( - mat => mat == -1 ? -1 : mat + materialOffsets[pair.vimIndex])?.ToList() + pair => new VimMesh( + indices: pair.mesh.indices, + vertices: pair.mesh.vertices, + submeshIndexOffsets: pair.mesh.submeshIndexOffsets, + submeshMaterials: pair.mesh.submeshMaterials.Select( + mat => mat == -1 ? -1 : mat + materialOffsets[pair.vimIndex]).ToArray() ) ) .ToList()); @@ -161,7 +161,7 @@ public static DocumentBuilder MergeVimScenes( vimTransforms = gridTransforms.Zip(vimTransforms, (g, t) => g * t).ToArray(); } - var meshCounts = vims.Select(v => v.Meshes.Count).ToArray(); + var meshCounts = vims.Select(v => v.Meshes.Length).ToArray(); var meshOffsets = meshCounts.PostAccumulate((x, y) => x + y).DropLast(); // Merge the instances @@ -171,7 +171,7 @@ public static DocumentBuilder MergeVimScenes( var allIdentity = vimTransforms.All(t => t.IsIdentity); db.Geometry.AddInstances( vims - .SelectMany((vim, vimIndex) => vim.VimNodes.Select(node => (node, vimIndex))) + .SelectMany((vim, vimIndex) => vim.Nodes.Select(node => (node, vimIndex))) .Select(pair => new DocumentBuilder.Instance() { ParentIndex = -1, diff --git a/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs b/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs index 8dd3db31..b1ed6d12 100644 --- a/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs +++ b/src/cs/vim/Vim.Format/Merge/MergedTableBuilder.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Threading; using Vim.BFastLib; -using Vim.LinqArray; using Vim.Util; namespace Vim.Format.Merge @@ -25,9 +24,8 @@ public void AddTable(EntityTable entityTable, Dictionary entit Debug.Assert(entityIndexOffsets[entityTable] == NumRows); // Add index columns from the entity table - foreach (var k in entityTable.IndexColumns.Keys) + foreach (var col in entityTable.IndexColumns) { - var col = entityTable.IndexColumns[k]; var indexColumnFullName = col.Name; if (!IndexColumns.ContainsKey(indexColumnFullName)) @@ -43,29 +41,27 @@ public void AddTable(EntityTable entityTable, Dictionary entit } // Add data columns from the entity table - foreach (var colName in entityTable.DataColumns.Keys) + foreach (var col in entityTable.DataColumns) { - var col = entityTable.DataColumns[colName]; - if (!DataColumns.ContainsKey(colName)) + if (!DataColumns.ContainsKey(col.Name)) { - DataColumns[colName] = col; + DataColumns[col.Name] = col; } else { - var cur = DataColumns[colName]; - DataColumns[colName] = cur.ConcatDataColumnBuffers(col, colName.GetTypePrefix()); + var cur = DataColumns[col.Name]; + DataColumns[col.Name] = cur.ConcatDataColumnBuffers(col, SerializableEntityTable.GetTypeFromName(col.Name)); } } // Add string columns from the entity table - foreach (var k in entityTable.StringColumns.Keys) + foreach (var col in entityTable.StringColumns) { - if (!StringColumns.ContainsKey(k)) - StringColumns.Add(k, Enumerable.Repeat("", NumRows).ToList()); + if (!StringColumns.ContainsKey(col.Name)) + StringColumns.Add(col.Name, Enumerable.Repeat("", NumRows).ToList()); - var col = entityTable.StringColumns[k]; Debug.Assert(col.Array.Length == entityTable.NumRows); - var vals = StringColumns[k]; + var vals = StringColumns[col.Name]; foreach (var v in col.Array) vals.Add(entityTable.Document.GetString(v)); } @@ -74,8 +70,8 @@ public void AddTable(EntityTable entityTable, Dictionary entit foreach (var kv in DataColumns) { var colName = kv.Key; - var typePrefix = colName.GetTypePrefix(); - if (!entityTable.DataColumns.ContainsKey(colName)) + var typePrefix = SerializableEntityTable.GetTypeFromName(colName); + if (entityTable.DataColumns.All(c => c.Name != colName)) { var cur = DataColumns[colName]; var defaultBuffer = ColumnExtensions.CreateDefaultDataColumnBuffer(entityTable.NumRows, typePrefix); @@ -85,13 +81,13 @@ public void AddTable(EntityTable entityTable, Dictionary entit foreach (var kv in IndexColumns) { - if (!entityTable.IndexColumns.ContainsKey(kv.Key)) + if (entityTable.IndexColumns.All(c => c.Name != kv.Key)) IndexColumns[kv.Key].AddRange(Enumerable.Repeat(-1, entityTable.NumRows)); } foreach (var kv in StringColumns) { - if (!entityTable.StringColumns.ContainsKey(kv.Key)) + if (entityTable.StringColumns.All(c => c.Name != kv.Key)) StringColumns[kv.Key].AddRange(Enumerable.Repeat("", entityTable.NumRows)); } diff --git a/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs b/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs index 7dc6b65f..48e98ae6 100644 --- a/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs +++ b/src/cs/vim/Vim.Format/Merge/RemappedEntityTableBuilder.cs @@ -96,7 +96,7 @@ private static RemappedEntityTableBuilder CreateRemapped(EntityTableBuilder e { var colName = kv.Key; var col = kv.Value; - var typePrefix = colName.GetTypePrefix(); + var typePrefix = SerializableEntityTable.GetTypeFromName(colName); var newCol = col.CopyDataColumn(typePrefix, retainedIndices); remapped.AddDataColumn(colName, newCol); } diff --git a/src/cs/vim/Vim.Format/ObjectModel/ElementIndexMaps.cs b/src/cs/vim/Vim.Format/ObjectModel/ElementIndexMaps.cs index 4ad3c786..9c29d851 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/ElementIndexMaps.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/ElementIndexMaps.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Vim.Util; using IndexMap = System.Collections.Generic.Dictionary; @@ -90,7 +91,7 @@ public ElementIndexMaps(EntityTableSet entityTables, bool inParallel = true) public static DictionaryOfLists GetElementIndicesMap(EntityTable et) { var indicesMap = new DictionaryOfLists(); - var elementIndices = et?.IndexColumns[ElementIndexColumnName]?.GetTypedData(); + var elementIndices = et?.IndexColumns.FirstOrDefault(col => col.Name == ElementIndexColumnName)?.GetTypedData(); if (elementIndices == null) return indicesMap; for (var i = 0; i < elementIndices.Length; ++i) @@ -112,7 +113,7 @@ public static DictionaryOfLists GetElementIndicesMap(EntityTable_v2 et public static IndexMap GetElementIndexMap(EntityTable et) { var indexMap = new IndexMap(); - var elementIndices = et?.IndexColumns[ElementIndexColumnName]?.GetTypedData(); + var elementIndices = et?.IndexColumns.FirstOrDefault(col => col.Name == ElementIndexColumnName)?.GetTypedData(); if (elementIndices == null) return indexMap; for (var i = 0; i < elementIndices.Length; ++i) diff --git a/src/cs/vim/Vim.Format/ObjectModel/ElementInfo.cs b/src/cs/vim/Vim.Format/ObjectModel/ElementInfo.cs index 0bcf84da..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.G3d; +using System.Linq; namespace Vim.Format.ObjectModel { @@ -29,8 +29,8 @@ public int FamilyInstanceIndex } } - private IList _parameterIndices; - public IList ParameterIndices + private int[] _parameterIndices; + public int[] ParameterIndices { get { @@ -38,7 +38,8 @@ public IList ParameterIndices return _parameterIndices; _parameterIndices = (DocumentModel.ElementIndexMaps.ParameterIndicesFromElementIndex - .TryGetValue(ElementIndex, out var parameterIndices) ? parameterIndices.ToArray() : Array.Empty()); + .TryGetValue(ElementIndex, out var parameterIndices) ? parameterIndices : new List()) + .ToArray(); return _parameterIndices; } @@ -107,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); + 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 df5d2634..6282981a 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelExtensions.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelExtensions.cs @@ -3,9 +3,7 @@ using System.Data; using System.IO; using System.Linq; -using Vim.G3d; using Vim.Util; -using Vim.LinqArray; namespace Vim.Format.ObjectModel { @@ -27,13 +25,7 @@ public static string GetUrn(this BimDocument bd) => Urn.GetBimDocumentUrn(Urn.VimNID, bd); public static Element CreateSyntheticElement(string name, string type) - => new Element - { - Id = VimConstants.SyntheticElementId, - Name = name, - Type = type, - UniqueId = $"{name}_{type}" // NOTE: we need to assign a UniqueId for merging purposes. - }; + => new Element { Id = VimConstants.SyntheticElementId, Name = name, Type = type }; public static Element CreateParameterHolderElement(string bimDocumentName) => CreateSyntheticElement(bimDocumentName, VimConstants.BimDocumentParameterHolderElementType); @@ -47,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 0b7b0a15..ec6ddd7c 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/ObjectModelGenerated.cs @@ -1,11 +1,11 @@ // AUTO-GENERATED FILE, DO NOT MODIFY. // ReSharper disable All using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using Vim.Math3d; -using Vim.G3d; +using Vim.Format.ObjectModel; +using Vim.Util; namespace Vim.Format.ObjectModel { // AUTO-GENERATED @@ -1892,10 +1892,10 @@ public partial class DocumentModel public EntityTable AssetEntityTable { get; } - public IList 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 IList AssetList { get; } + public Asset[] AssetList { get; } public Asset GetAsset(int n) { if (n < 0) return null; @@ -1911,14 +1911,14 @@ public Asset GetAsset(int n) public EntityTable DisplayUnitEntityTable { get; } - public IList DisplayUnitSpec { get; } + public String[] DisplayUnitSpec { get; } public String GetDisplayUnitSpec(int index, String defaultValue = "") => DisplayUnitSpec?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList DisplayUnitType { get; } + public String[] DisplayUnitType { get; } public String GetDisplayUnitType(int index, String defaultValue = "") => DisplayUnitType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList DisplayUnitList { get; } + public DisplayUnit[] DisplayUnitList { get; } public DisplayUnit GetDisplayUnit(int n) { if (n < 0) return null; @@ -1936,28 +1936,28 @@ public DisplayUnit GetDisplayUnit(int n) public EntityTable ParameterDescriptorEntityTable { get; } - public IList ParameterDescriptorName { get; } + public String[] ParameterDescriptorName { get; } public String GetParameterDescriptorName(int index, String defaultValue = "") => ParameterDescriptorName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ParameterDescriptorGroup { get; } + public String[] ParameterDescriptorGroup { get; } public String GetParameterDescriptorGroup(int index, String defaultValue = "") => ParameterDescriptorGroup?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ParameterDescriptorParameterType { get; } + public String[] ParameterDescriptorParameterType { get; } public String GetParameterDescriptorParameterType(int index, String defaultValue = "") => ParameterDescriptorParameterType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ParameterDescriptorIsInstance { get; } + public Boolean[] ParameterDescriptorIsInstance { get; } public Boolean GetParameterDescriptorIsInstance(int index, Boolean defaultValue = default) => ParameterDescriptorIsInstance?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ParameterDescriptorIsShared { get; } + public Boolean[] ParameterDescriptorIsShared { get; } public Boolean GetParameterDescriptorIsShared(int index, Boolean defaultValue = default) => ParameterDescriptorIsShared?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ParameterDescriptorIsReadOnly { get; } + public Boolean[] ParameterDescriptorIsReadOnly { get; } public Boolean GetParameterDescriptorIsReadOnly(int index, Boolean defaultValue = default) => ParameterDescriptorIsReadOnly?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ParameterDescriptorFlags { get; } + public Int32[] ParameterDescriptorFlags { get; } public Int32 GetParameterDescriptorFlags(int index, Int32 defaultValue = default) => ParameterDescriptorFlags?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ParameterDescriptorGuid { get; } + public String[] ParameterDescriptorGuid { get; } public String GetParameterDescriptorGuid(int index, String defaultValue = "") => ParameterDescriptorGuid?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ParameterDescriptorStorageType { get; } + public Int32[] ParameterDescriptorStorageType { get; } public Int32 GetParameterDescriptorStorageType(int index, Int32 defaultValue = default) => ParameterDescriptorStorageType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList ParameterDescriptorList { get; } + public ParameterDescriptor[] ParameterDescriptorList { get; } public ParameterDescriptor GetParameterDescriptor(int n) { if (n < 0) return null; @@ -1982,14 +1982,14 @@ public ParameterDescriptor GetParameterDescriptor(int n) public EntityTable ParameterEntityTable { get; } - public IList ParameterValue { get; } + public String[] ParameterValue { get; } public String GetParameterValue(int index, String defaultValue = "") => ParameterValue?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ParameterParameterDescriptorIndex { get; } + public int[] ParameterParameterDescriptorIndex { get; } public int GetParameterParameterDescriptorIndex(int index) => ParameterParameterDescriptorIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList ParameterList { get; } + public Parameter[] ParameterList { get; } public Parameter GetParameter(int n) { if (n < 0) return null; @@ -2007,48 +2007,48 @@ public Parameter GetParameter(int n) public EntityTable ElementEntityTable { get; } - public IList ElementId { get; } + public Int64[] ElementId { get; } public Int64 GetElementId(int index, Int64 defaultValue = default) => ElementId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ElementType { get; } + public String[] ElementType { get; } public String GetElementType(int index, String defaultValue = "") => ElementType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ElementName { get; } + public String[] ElementName { get; } public String GetElementName(int index, String defaultValue = "") => ElementName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ElementUniqueId { get; } + public String[] ElementUniqueId { get; } public String GetElementUniqueId(int index, String defaultValue = "") => ElementUniqueId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ElementLocation_X { get; } + public Single[] ElementLocation_X { get; } public Single GetElementLocation_X(int index, Single defaultValue = default) => ElementLocation_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ElementLocation_Y { get; } + public Single[] ElementLocation_Y { get; } public Single GetElementLocation_Y(int index, Single defaultValue = default) => ElementLocation_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ElementLocation_Z { get; } + public Single[] ElementLocation_Z { get; } public Single GetElementLocation_Z(int index, Single defaultValue = default) => ElementLocation_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ElementFamilyName { get; } + public String[] ElementFamilyName { get; } public String GetElementFamilyName(int index, String defaultValue = "") => ElementFamilyName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ElementIsPinned { get; } + public Boolean[] ElementIsPinned { get; } public Boolean GetElementIsPinned(int index, Boolean defaultValue = default) => ElementIsPinned?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ElementLevelIndex { get; } + public int[] ElementLevelIndex { get; } public int GetElementLevelIndex(int index) => ElementLevelIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList ElementPhaseCreatedIndex { get; } + public int[] ElementPhaseCreatedIndex { get; } public int GetElementPhaseCreatedIndex(int index) => ElementPhaseCreatedIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList ElementPhaseDemolishedIndex { get; } + public int[] ElementPhaseDemolishedIndex { get; } public int GetElementPhaseDemolishedIndex(int index) => ElementPhaseDemolishedIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList ElementCategoryIndex { get; } + public int[] ElementCategoryIndex { get; } public int GetElementCategoryIndex(int index) => ElementCategoryIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList ElementWorksetIndex { get; } + public int[] ElementWorksetIndex { get; } public int GetElementWorksetIndex(int index) => ElementWorksetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList ElementDesignOptionIndex { get; } + public int[] ElementDesignOptionIndex { get; } public int GetElementDesignOptionIndex(int index) => ElementDesignOptionIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList ElementOwnerViewIndex { get; } + public int[] ElementOwnerViewIndex { get; } public int GetElementOwnerViewIndex(int index) => ElementOwnerViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList ElementGroupIndex { get; } + public int[] ElementGroupIndex { get; } public int GetElementGroupIndex(int index) => ElementGroupIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList ElementAssemblyInstanceIndex { get; } + public int[] ElementAssemblyInstanceIndex { get; } public int GetElementAssemblyInstanceIndex(int index) => ElementAssemblyInstanceIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList ElementBimDocumentIndex { get; } + public int[] ElementBimDocumentIndex { get; } public int GetElementBimDocumentIndex(int index) => ElementBimDocumentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList ElementList { get; } + public Element[] ElementList { get; } public Element GetElement(int n) { if (n < 0) return null; @@ -2083,24 +2083,24 @@ public Element GetElement(int n) public EntityTable WorksetEntityTable { get; } - public IList WorksetId { get; } + public Int32[] WorksetId { get; } public Int32 GetWorksetId(int index, Int32 defaultValue = default) => WorksetId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList WorksetName { get; } + public String[] WorksetName { get; } public String GetWorksetName(int index, String defaultValue = "") => WorksetName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList WorksetKind { get; } + public String[] WorksetKind { get; } public String GetWorksetKind(int index, String defaultValue = "") => WorksetKind?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList WorksetIsOpen { get; } + public Boolean[] WorksetIsOpen { get; } public Boolean GetWorksetIsOpen(int index, Boolean defaultValue = default) => WorksetIsOpen?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList WorksetIsEditable { get; } + public Boolean[] WorksetIsEditable { get; } public Boolean GetWorksetIsEditable(int index, Boolean defaultValue = default) => WorksetIsEditable?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList WorksetOwner { get; } + public String[] WorksetOwner { get; } public String GetWorksetOwner(int index, String defaultValue = "") => WorksetOwner?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList WorksetUniqueId { get; } + public String[] WorksetUniqueId { get; } public String GetWorksetUniqueId(int index, String defaultValue = "") => WorksetUniqueId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList WorksetList { get; } + public Workset[] WorksetList { get; } public Workset GetWorkset(int n) { if (n < 0) return null; @@ -2123,18 +2123,18 @@ public Workset GetWorkset(int n) public EntityTable AssemblyInstanceEntityTable { get; } - public IList AssemblyInstanceAssemblyTypeName { get; } + public String[] AssemblyInstanceAssemblyTypeName { get; } public String GetAssemblyInstanceAssemblyTypeName(int index, String defaultValue = "") => AssemblyInstanceAssemblyTypeName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList AssemblyInstancePosition_X { get; } + public Single[] AssemblyInstancePosition_X { get; } public Single GetAssemblyInstancePosition_X(int index, Single defaultValue = default) => AssemblyInstancePosition_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList AssemblyInstancePosition_Y { get; } + public Single[] AssemblyInstancePosition_Y { get; } public Single GetAssemblyInstancePosition_Y(int index, Single defaultValue = default) => AssemblyInstancePosition_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList AssemblyInstancePosition_Z { get; } + public Single[] AssemblyInstancePosition_Z { get; } public Single GetAssemblyInstancePosition_Z(int index, Single defaultValue = default) => AssemblyInstancePosition_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList AssemblyInstanceList { get; } + public AssemblyInstance[] AssemblyInstanceList { get; } public AssemblyInstance GetAssemblyInstance(int n) { if (n < 0) return null; @@ -2154,18 +2154,18 @@ public AssemblyInstance GetAssemblyInstance(int n) public EntityTable GroupEntityTable { get; } - public IList GroupGroupType { get; } + public String[] GroupGroupType { get; } public String GetGroupGroupType(int index, String defaultValue = "") => GroupGroupType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList GroupPosition_X { get; } + public Single[] GroupPosition_X { get; } public Single GetGroupPosition_X(int index, Single defaultValue = default) => GroupPosition_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList GroupPosition_Y { get; } + public Single[] GroupPosition_Y { get; } public Single GetGroupPosition_Y(int index, Single defaultValue = default) => GroupPosition_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList GroupPosition_Z { get; } + public Single[] GroupPosition_Z { get; } public Single GetGroupPosition_Z(int index, Single defaultValue = default) => GroupPosition_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList GroupList { get; } + public Group[] GroupList { get; } public Group GetGroup(int n) { if (n < 0) return null; @@ -2185,12 +2185,12 @@ public Group GetGroup(int n) public EntityTable DesignOptionEntityTable { get; } - public IList DesignOptionIsPrimary { get; } + public Boolean[] DesignOptionIsPrimary { get; } public Boolean GetDesignOptionIsPrimary(int index, Boolean defaultValue = default) => DesignOptionIsPrimary?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList DesignOptionList { get; } + public DesignOption[] DesignOptionList { get; } public DesignOption GetDesignOption(int n) { if (n < 0) return null; @@ -2207,16 +2207,16 @@ public DesignOption GetDesignOption(int n) public EntityTable LevelEntityTable { get; } - public IList LevelElevation { get; } + public Double[] LevelElevation { get; } public Double GetLevelElevation(int index, Double defaultValue = default) => LevelElevation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList LevelFamilyTypeIndex { get; } + public int[] LevelFamilyTypeIndex { get; } public int GetLevelFamilyTypeIndex(int index) => LevelFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList LevelBuildingIndex { get; } + public int[] LevelBuildingIndex { get; } public int GetLevelBuildingIndex(int index) => LevelBuildingIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList LevelList { get; } + public Level[] LevelList { get; } public Level GetLevel(int n) { if (n < 0) return null; @@ -2235,10 +2235,10 @@ public Level GetLevel(int n) public EntityTable PhaseEntityTable { get; } - public IList 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 IList PhaseList { get; } + public Phase[] PhaseList { get; } public Phase GetPhase(int n) { if (n < 0) return null; @@ -2254,26 +2254,26 @@ public Phase GetPhase(int n) public EntityTable RoomEntityTable { get; } - public IList RoomBaseOffset { get; } + public Double[] RoomBaseOffset { get; } public Double GetRoomBaseOffset(int index, Double defaultValue = default) => RoomBaseOffset?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList RoomLimitOffset { get; } + public Double[] RoomLimitOffset { get; } public Double GetRoomLimitOffset(int index, Double defaultValue = default) => RoomLimitOffset?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList RoomUnboundedHeight { get; } + public Double[] RoomUnboundedHeight { get; } public Double GetRoomUnboundedHeight(int index, Double defaultValue = default) => RoomUnboundedHeight?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList RoomVolume { get; } + public Double[] RoomVolume { get; } public Double GetRoomVolume(int index, Double defaultValue = default) => RoomVolume?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList RoomPerimeter { get; } + public Double[] RoomPerimeter { get; } public Double GetRoomPerimeter(int index, Double defaultValue = default) => RoomPerimeter?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList RoomArea { get; } + public Double[] RoomArea { get; } public Double GetRoomArea(int index, Double defaultValue = default) => RoomArea?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList RoomNumber { get; } + public String[] RoomNumber { get; } public String GetRoomNumber(int index, String defaultValue = "") => RoomNumber?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList RoomUpperLimitIndex { get; } + public int[] RoomUpperLimitIndex { get; } public int GetRoomUpperLimitIndex(int index) => RoomUpperLimitIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList RoomList { get; } + public Room[] RoomList { get; } public Room GetRoom(int n) { if (n < 0) return null; @@ -2297,72 +2297,72 @@ public Room GetRoom(int n) public EntityTable BimDocumentEntityTable { get; } - public IList BimDocumentTitle { get; } + public String[] BimDocumentTitle { get; } public String GetBimDocumentTitle(int index, String defaultValue = "") => BimDocumentTitle?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentIsMetric { get; } + public Boolean[] BimDocumentIsMetric { get; } public Boolean GetBimDocumentIsMetric(int index, Boolean defaultValue = default) => BimDocumentIsMetric?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentGuid { get; } + public String[] BimDocumentGuid { get; } public String GetBimDocumentGuid(int index, String defaultValue = "") => BimDocumentGuid?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentNumSaves { get; } + public Int32[] BimDocumentNumSaves { get; } public Int32 GetBimDocumentNumSaves(int index, Int32 defaultValue = default) => BimDocumentNumSaves?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentIsLinked { get; } + public Boolean[] BimDocumentIsLinked { get; } public Boolean GetBimDocumentIsLinked(int index, Boolean defaultValue = default) => BimDocumentIsLinked?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentIsDetached { get; } + public Boolean[] BimDocumentIsDetached { get; } public Boolean GetBimDocumentIsDetached(int index, Boolean defaultValue = default) => BimDocumentIsDetached?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentIsWorkshared { get; } + public Boolean[] BimDocumentIsWorkshared { get; } public Boolean GetBimDocumentIsWorkshared(int index, Boolean defaultValue = default) => BimDocumentIsWorkshared?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentPathName { get; } + public String[] BimDocumentPathName { get; } public String GetBimDocumentPathName(int index, String defaultValue = "") => BimDocumentPathName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentLatitude { get; } + public Double[] BimDocumentLatitude { get; } public Double GetBimDocumentLatitude(int index, Double defaultValue = default) => BimDocumentLatitude?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentLongitude { get; } + public Double[] BimDocumentLongitude { get; } public Double GetBimDocumentLongitude(int index, Double defaultValue = default) => BimDocumentLongitude?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentTimeZone { get; } + public Double[] BimDocumentTimeZone { get; } public Double GetBimDocumentTimeZone(int index, Double defaultValue = default) => BimDocumentTimeZone?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentPlaceName { get; } + public String[] BimDocumentPlaceName { get; } public String GetBimDocumentPlaceName(int index, String defaultValue = "") => BimDocumentPlaceName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentWeatherStationName { get; } + public String[] BimDocumentWeatherStationName { get; } public String GetBimDocumentWeatherStationName(int index, String defaultValue = "") => BimDocumentWeatherStationName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentElevation { get; } + public Double[] BimDocumentElevation { get; } public Double GetBimDocumentElevation(int index, Double defaultValue = default) => BimDocumentElevation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentProjectLocation { get; } + public String[] BimDocumentProjectLocation { get; } public String GetBimDocumentProjectLocation(int index, String defaultValue = "") => BimDocumentProjectLocation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentIssueDate { get; } + public String[] BimDocumentIssueDate { get; } public String GetBimDocumentIssueDate(int index, String defaultValue = "") => BimDocumentIssueDate?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentStatus { get; } + public String[] BimDocumentStatus { get; } public String GetBimDocumentStatus(int index, String defaultValue = "") => BimDocumentStatus?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentClientName { get; } + public String[] BimDocumentClientName { get; } public String GetBimDocumentClientName(int index, String defaultValue = "") => BimDocumentClientName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentAddress { get; } + public String[] BimDocumentAddress { get; } public String GetBimDocumentAddress(int index, String defaultValue = "") => BimDocumentAddress?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentName { get; } + public String[] BimDocumentName { get; } public String GetBimDocumentName(int index, String defaultValue = "") => BimDocumentName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentNumber { get; } + public String[] BimDocumentNumber { get; } public String GetBimDocumentNumber(int index, String defaultValue = "") => BimDocumentNumber?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentAuthor { get; } + public String[] BimDocumentAuthor { get; } public String GetBimDocumentAuthor(int index, String defaultValue = "") => BimDocumentAuthor?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentBuildingName { get; } + public String[] BimDocumentBuildingName { get; } public String GetBimDocumentBuildingName(int index, String defaultValue = "") => BimDocumentBuildingName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentOrganizationName { get; } + public String[] BimDocumentOrganizationName { get; } public String GetBimDocumentOrganizationName(int index, String defaultValue = "") => BimDocumentOrganizationName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentOrganizationDescription { get; } + public String[] BimDocumentOrganizationDescription { get; } public String GetBimDocumentOrganizationDescription(int index, String defaultValue = "") => BimDocumentOrganizationDescription?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentProduct { get; } + public String[] BimDocumentProduct { get; } public String GetBimDocumentProduct(int index, String defaultValue = "") => BimDocumentProduct?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentVersion { get; } + public String[] BimDocumentVersion { get; } public String GetBimDocumentVersion(int index, String defaultValue = "") => BimDocumentVersion?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentUser { get; } + public String[] BimDocumentUser { get; } public String GetBimDocumentUser(int index, String defaultValue = "") => BimDocumentUser?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BimDocumentActiveViewIndex { get; } + public int[] BimDocumentActiveViewIndex { get; } public int GetBimDocumentActiveViewIndex(int index) => BimDocumentActiveViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList BimDocumentOwnerFamilyIndex { get; } + public int[] BimDocumentOwnerFamilyIndex { get; } public int GetBimDocumentOwnerFamilyIndex(int index) => BimDocumentOwnerFamilyIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList BimDocumentParentIndex { get; } + public int[] BimDocumentParentIndex { get; } public int GetBimDocumentParentIndex(int index) => BimDocumentParentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList BimDocumentList { get; } + public BimDocument[] BimDocumentList { get; } public BimDocument GetBimDocument(int n) { if (n < 0) return null; @@ -2409,12 +2409,12 @@ public BimDocument GetBimDocument(int n) public EntityTable DisplayUnitInBimDocumentEntityTable { get; } - public IList DisplayUnitInBimDocumentDisplayUnitIndex { get; } + public int[] DisplayUnitInBimDocumentDisplayUnitIndex { get; } public int GetDisplayUnitInBimDocumentDisplayUnitIndex(int index) => DisplayUnitInBimDocumentDisplayUnitIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList DisplayUnitInBimDocumentList { get; } + public DisplayUnitInBimDocument[] DisplayUnitInBimDocumentList { get; } public DisplayUnitInBimDocument GetDisplayUnitInBimDocument(int n) { if (n < 0) return null; @@ -2431,14 +2431,14 @@ public DisplayUnitInBimDocument GetDisplayUnitInBimDocument(int n) public EntityTable PhaseOrderInBimDocumentEntityTable { get; } - public IList PhaseOrderInBimDocumentOrderIndex { get; } + public Int32[] PhaseOrderInBimDocumentOrderIndex { get; } public Int32 GetPhaseOrderInBimDocumentOrderIndex(int index, Int32 defaultValue = default) => PhaseOrderInBimDocumentOrderIndex?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList PhaseOrderInBimDocumentPhaseIndex { get; } + public int[] PhaseOrderInBimDocumentPhaseIndex { get; } public int GetPhaseOrderInBimDocumentPhaseIndex(int index) => PhaseOrderInBimDocumentPhaseIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList PhaseOrderInBimDocumentList { get; } + public PhaseOrderInBimDocument[] PhaseOrderInBimDocumentList { get; } public PhaseOrderInBimDocument GetPhaseOrderInBimDocument(int n) { if (n < 0) return null; @@ -2456,26 +2456,26 @@ public PhaseOrderInBimDocument GetPhaseOrderInBimDocument(int n) public EntityTable CategoryEntityTable { get; } - public IList CategoryName { get; } + public String[] CategoryName { get; } public String GetCategoryName(int index, String defaultValue = "") => CategoryName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CategoryId { get; } + public Int64[] CategoryId { get; } public Int64 GetCategoryId(int index, Int64 defaultValue = default) => CategoryId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CategoryCategoryType { get; } + public String[] CategoryCategoryType { get; } public String GetCategoryCategoryType(int index, String defaultValue = "") => CategoryCategoryType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CategoryLineColor_X { get; } + public Double[] CategoryLineColor_X { get; } public Double GetCategoryLineColor_X(int index, Double defaultValue = default) => CategoryLineColor_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CategoryLineColor_Y { get; } + public Double[] CategoryLineColor_Y { get; } public Double GetCategoryLineColor_Y(int index, Double defaultValue = default) => CategoryLineColor_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CategoryLineColor_Z { get; } + public Double[] CategoryLineColor_Z { get; } public Double GetCategoryLineColor_Z(int index, Double defaultValue = default) => CategoryLineColor_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CategoryBuiltInCategory { get; } + public String[] CategoryBuiltInCategory { get; } public String GetCategoryBuiltInCategory(int index, String defaultValue = "") => CategoryBuiltInCategory?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CategoryParentIndex { get; } + public int[] CategoryParentIndex { get; } public int GetCategoryParentIndex(int index) => CategoryParentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList CategoryList { get; } + public Category[] CategoryList { get; } public Category GetCategory(int n) { if (n < 0) return null; @@ -2499,20 +2499,20 @@ public Category GetCategory(int n) public EntityTable FamilyEntityTable { get; } - public IList FamilyStructuralMaterialType { get; } + public String[] FamilyStructuralMaterialType { get; } public String GetFamilyStructuralMaterialType(int index, String defaultValue = "") => FamilyStructuralMaterialType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyStructuralSectionShape { get; } + public String[] FamilyStructuralSectionShape { get; } public String GetFamilyStructuralSectionShape(int index, String defaultValue = "") => FamilyStructuralSectionShape?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyIsSystemFamily { get; } + public Boolean[] FamilyIsSystemFamily { get; } public Boolean GetFamilyIsSystemFamily(int index, Boolean defaultValue = default) => FamilyIsSystemFamily?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyIsInPlace { get; } + public Boolean[] FamilyIsInPlace { get; } public Boolean GetFamilyIsInPlace(int index, Boolean defaultValue = default) => FamilyIsInPlace?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyFamilyCategoryIndex { get; } + public int[] FamilyFamilyCategoryIndex { get; } public int GetFamilyFamilyCategoryIndex(int index) => FamilyFamilyCategoryIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList FamilyList { get; } + public Family[] FamilyList { get; } public Family GetFamily(int n) { if (n < 0) return null; @@ -2533,16 +2533,16 @@ public Family GetFamily(int n) public EntityTable FamilyTypeEntityTable { get; } - public IList FamilyTypeIsSystemFamilyType { get; } + public Boolean[] FamilyTypeIsSystemFamilyType { get; } public Boolean GetFamilyTypeIsSystemFamilyType(int index, Boolean defaultValue = default) => FamilyTypeIsSystemFamilyType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyTypeFamilyIndex { get; } + public int[] FamilyTypeFamilyIndex { get; } public int GetFamilyTypeFamilyIndex(int index) => FamilyTypeFamilyIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList FamilyTypeCompoundStructureIndex { get; } + public int[] FamilyTypeCompoundStructureIndex { get; } public int GetFamilyTypeCompoundStructureIndex(int index) => FamilyTypeCompoundStructureIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList FamilyTypeList { get; } + public FamilyType[] FamilyTypeList { get; } public FamilyType GetFamilyType(int n) { if (n < 0) return null; @@ -2561,66 +2561,66 @@ public FamilyType GetFamilyType(int n) public EntityTable FamilyInstanceEntityTable { get; } - public IList FamilyInstanceFacingFlipped { get; } + public Boolean[] FamilyInstanceFacingFlipped { get; } public Boolean GetFamilyInstanceFacingFlipped(int index, Boolean defaultValue = default) => FamilyInstanceFacingFlipped?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceFacingOrientation_X { get; } + public Single[] FamilyInstanceFacingOrientation_X { get; } public Single GetFamilyInstanceFacingOrientation_X(int index, Single defaultValue = default) => FamilyInstanceFacingOrientation_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceFacingOrientation_Y { get; } + public Single[] FamilyInstanceFacingOrientation_Y { get; } public Single GetFamilyInstanceFacingOrientation_Y(int index, Single defaultValue = default) => FamilyInstanceFacingOrientation_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceFacingOrientation_Z { get; } + public Single[] FamilyInstanceFacingOrientation_Z { get; } public Single GetFamilyInstanceFacingOrientation_Z(int index, Single defaultValue = default) => FamilyInstanceFacingOrientation_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceHandFlipped { get; } + public Boolean[] FamilyInstanceHandFlipped { get; } public Boolean GetFamilyInstanceHandFlipped(int index, Boolean defaultValue = default) => FamilyInstanceHandFlipped?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceMirrored { get; } + public Boolean[] FamilyInstanceMirrored { get; } public Boolean GetFamilyInstanceMirrored(int index, Boolean defaultValue = default) => FamilyInstanceMirrored?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceHasModifiedGeometry { get; } + public Boolean[] FamilyInstanceHasModifiedGeometry { get; } public Boolean GetFamilyInstanceHasModifiedGeometry(int index, Boolean defaultValue = default) => FamilyInstanceHasModifiedGeometry?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceScale { get; } + public Single[] FamilyInstanceScale { get; } public Single GetFamilyInstanceScale(int index, Single defaultValue = default) => FamilyInstanceScale?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceBasisX_X { get; } + public Single[] FamilyInstanceBasisX_X { get; } public Single GetFamilyInstanceBasisX_X(int index, Single defaultValue = default) => FamilyInstanceBasisX_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceBasisX_Y { get; } + public Single[] FamilyInstanceBasisX_Y { get; } public Single GetFamilyInstanceBasisX_Y(int index, Single defaultValue = default) => FamilyInstanceBasisX_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceBasisX_Z { get; } + public Single[] FamilyInstanceBasisX_Z { get; } public Single GetFamilyInstanceBasisX_Z(int index, Single defaultValue = default) => FamilyInstanceBasisX_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceBasisY_X { get; } + public Single[] FamilyInstanceBasisY_X { get; } public Single GetFamilyInstanceBasisY_X(int index, Single defaultValue = default) => FamilyInstanceBasisY_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceBasisY_Y { get; } + public Single[] FamilyInstanceBasisY_Y { get; } public Single GetFamilyInstanceBasisY_Y(int index, Single defaultValue = default) => FamilyInstanceBasisY_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceBasisY_Z { get; } + public Single[] FamilyInstanceBasisY_Z { get; } public Single GetFamilyInstanceBasisY_Z(int index, Single defaultValue = default) => FamilyInstanceBasisY_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceBasisZ_X { get; } + public Single[] FamilyInstanceBasisZ_X { get; } public Single GetFamilyInstanceBasisZ_X(int index, Single defaultValue = default) => FamilyInstanceBasisZ_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceBasisZ_Y { get; } + public Single[] FamilyInstanceBasisZ_Y { get; } public Single GetFamilyInstanceBasisZ_Y(int index, Single defaultValue = default) => FamilyInstanceBasisZ_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceBasisZ_Z { get; } + public Single[] FamilyInstanceBasisZ_Z { get; } public Single GetFamilyInstanceBasisZ_Z(int index, Single defaultValue = default) => FamilyInstanceBasisZ_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceTranslation_X { get; } + public Single[] FamilyInstanceTranslation_X { get; } public Single GetFamilyInstanceTranslation_X(int index, Single defaultValue = default) => FamilyInstanceTranslation_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceTranslation_Y { get; } + public Single[] FamilyInstanceTranslation_Y { get; } public Single GetFamilyInstanceTranslation_Y(int index, Single defaultValue = default) => FamilyInstanceTranslation_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceTranslation_Z { get; } + public Single[] FamilyInstanceTranslation_Z { get; } public Single GetFamilyInstanceTranslation_Z(int index, Single defaultValue = default) => FamilyInstanceTranslation_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceHandOrientation_X { get; } + public Single[] FamilyInstanceHandOrientation_X { get; } public Single GetFamilyInstanceHandOrientation_X(int index, Single defaultValue = default) => FamilyInstanceHandOrientation_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceHandOrientation_Y { get; } + public Single[] FamilyInstanceHandOrientation_Y { get; } public Single GetFamilyInstanceHandOrientation_Y(int index, Single defaultValue = default) => FamilyInstanceHandOrientation_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceHandOrientation_Z { get; } + public Single[] FamilyInstanceHandOrientation_Z { get; } public Single GetFamilyInstanceHandOrientation_Z(int index, Single defaultValue = default) => FamilyInstanceHandOrientation_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList FamilyInstanceFamilyTypeIndex { get; } + public int[] FamilyInstanceFamilyTypeIndex { get; } public int GetFamilyInstanceFamilyTypeIndex(int index) => FamilyInstanceFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList FamilyInstanceHostIndex { get; } + public int[] FamilyInstanceHostIndex { get; } public int GetFamilyInstanceHostIndex(int index) => FamilyInstanceHostIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList FamilyInstanceFromRoomIndex { get; } + public int[] FamilyInstanceFromRoomIndex { get; } public int GetFamilyInstanceFromRoomIndex(int index) => FamilyInstanceFromRoomIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList FamilyInstanceToRoomIndex { get; } + public int[] FamilyInstanceToRoomIndex { get; } public int GetFamilyInstanceToRoomIndex(int index) => FamilyInstanceToRoomIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList FamilyInstanceSuperComponentIndex { get; } + public int[] FamilyInstanceSuperComponentIndex { get; } public int GetFamilyInstanceSuperComponentIndex(int index) => FamilyInstanceSuperComponentIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList FamilyInstanceList { get; } + public FamilyInstance[] FamilyInstanceList { get; } public FamilyInstance GetFamilyInstance(int n) { if (n < 0) return null; @@ -2664,60 +2664,60 @@ public FamilyInstance GetFamilyInstance(int n) public EntityTable ViewEntityTable { get; } - public IList ViewTitle { get; } + public String[] ViewTitle { get; } public String GetViewTitle(int index, String defaultValue = "") => ViewTitle?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewViewType { get; } + public String[] ViewViewType { get; } public String GetViewViewType(int index, String defaultValue = "") => ViewViewType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewUp_X { get; } + public Double[] ViewUp_X { get; } public Double GetViewUp_X(int index, Double defaultValue = default) => ViewUp_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewUp_Y { get; } + public Double[] ViewUp_Y { get; } public Double GetViewUp_Y(int index, Double defaultValue = default) => ViewUp_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewUp_Z { get; } + public Double[] ViewUp_Z { get; } public Double GetViewUp_Z(int index, Double defaultValue = default) => ViewUp_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewRight_X { get; } + public Double[] ViewRight_X { get; } public Double GetViewRight_X(int index, Double defaultValue = default) => ViewRight_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewRight_Y { get; } + public Double[] ViewRight_Y { get; } public Double GetViewRight_Y(int index, Double defaultValue = default) => ViewRight_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewRight_Z { get; } + public Double[] ViewRight_Z { get; } public Double GetViewRight_Z(int index, Double defaultValue = default) => ViewRight_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewOrigin_X { get; } + public Double[] ViewOrigin_X { get; } public Double GetViewOrigin_X(int index, Double defaultValue = default) => ViewOrigin_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewOrigin_Y { get; } + public Double[] ViewOrigin_Y { get; } public Double GetViewOrigin_Y(int index, Double defaultValue = default) => ViewOrigin_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewOrigin_Z { get; } + public Double[] ViewOrigin_Z { get; } public Double GetViewOrigin_Z(int index, Double defaultValue = default) => ViewOrigin_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewViewDirection_X { get; } + public Double[] ViewViewDirection_X { get; } public Double GetViewViewDirection_X(int index, Double defaultValue = default) => ViewViewDirection_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewViewDirection_Y { get; } + public Double[] ViewViewDirection_Y { get; } public Double GetViewViewDirection_Y(int index, Double defaultValue = default) => ViewViewDirection_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewViewDirection_Z { get; } + public Double[] ViewViewDirection_Z { get; } public Double GetViewViewDirection_Z(int index, Double defaultValue = default) => ViewViewDirection_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewViewPosition_X { get; } + public Double[] ViewViewPosition_X { get; } public Double GetViewViewPosition_X(int index, Double defaultValue = default) => ViewViewPosition_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewViewPosition_Y { get; } + public Double[] ViewViewPosition_Y { get; } public Double GetViewViewPosition_Y(int index, Double defaultValue = default) => ViewViewPosition_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewViewPosition_Z { get; } + public Double[] ViewViewPosition_Z { get; } public Double GetViewViewPosition_Z(int index, Double defaultValue = default) => ViewViewPosition_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewScale { get; } + public Double[] ViewScale { get; } public Double GetViewScale(int index, Double defaultValue = default) => ViewScale?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList 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 IList 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 IList 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 IList ViewDetailLevel { get; } + public Int32[] ViewDetailLevel { get; } public Int32 GetViewDetailLevel(int index, Int32 defaultValue = default) => ViewDetailLevel?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ViewCameraIndex { get; } + public int[] ViewCameraIndex { get; } public int GetViewCameraIndex(int index) => ViewCameraIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList ViewFamilyTypeIndex { get; } + public int[] ViewFamilyTypeIndex { get; } public int GetViewFamilyTypeIndex(int index) => ViewFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList ViewList { get; } + public View[] ViewList { get; } public View GetView(int n) { if (n < 0) return null; @@ -2758,12 +2758,12 @@ public View GetView(int n) public EntityTable ElementInViewEntityTable { get; } - public IList ElementInViewViewIndex { get; } + public int[] ElementInViewViewIndex { get; } public int GetElementInViewViewIndex(int index) => ElementInViewViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList ElementInViewList { get; } + public ElementInView[] ElementInViewList { get; } public ElementInView GetElementInView(int n) { if (n < 0) return null; @@ -2780,12 +2780,12 @@ public ElementInView GetElementInView(int n) public EntityTable ShapeInViewEntityTable { get; } - public IList ShapeInViewShapeIndex { get; } + public int[] ShapeInViewShapeIndex { get; } public int GetShapeInViewShapeIndex(int index) => ShapeInViewShapeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList ShapeInViewList { get; } + public ShapeInView[] ShapeInViewList { get; } public ShapeInView GetShapeInView(int n) { if (n < 0) return null; @@ -2802,12 +2802,12 @@ public ShapeInView GetShapeInView(int n) public EntityTable AssetInViewEntityTable { get; } - public IList AssetInViewAssetIndex { get; } + public int[] AssetInViewAssetIndex { get; } public int GetAssetInViewAssetIndex(int index) => AssetInViewAssetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList AssetInViewList { get; } + public AssetInView[] AssetInViewList { get; } public AssetInView GetAssetInView(int n) { if (n < 0) return null; @@ -2824,12 +2824,12 @@ public AssetInView GetAssetInView(int n) public EntityTable AssetInViewSheetEntityTable { get; } - public IList AssetInViewSheetAssetIndex { get; } + public int[] AssetInViewSheetAssetIndex { get; } public int GetAssetInViewSheetAssetIndex(int index) => AssetInViewSheetAssetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList AssetInViewSheetList { get; } + public AssetInViewSheet[] AssetInViewSheetList { get; } public AssetInViewSheet GetAssetInViewSheet(int n) { if (n < 0) return null; @@ -2846,24 +2846,24 @@ public AssetInViewSheet GetAssetInViewSheet(int n) public EntityTable LevelInViewEntityTable { get; } - public IList 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 IList 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 IList 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 IList 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 IList 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 IList 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 IList LevelInViewLevelIndex { get; } + public int[] LevelInViewLevelIndex { get; } public int GetLevelInViewLevelIndex(int index) => LevelInViewLevelIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList LevelInViewList { get; } + public LevelInView[] LevelInViewList { get; } public LevelInView GetLevelInView(int n) { if (n < 0) return null; @@ -2886,26 +2886,26 @@ public LevelInView GetLevelInView(int n) public EntityTable CameraEntityTable { get; } - public IList CameraId { get; } + public Int32[] CameraId { get; } public Int32 GetCameraId(int index, Int32 defaultValue = default) => CameraId?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CameraIsPerspective { get; } + public Int32[] CameraIsPerspective { get; } public Int32 GetCameraIsPerspective(int index, Int32 defaultValue = default) => CameraIsPerspective?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CameraVerticalExtent { get; } + public Double[] CameraVerticalExtent { get; } public Double GetCameraVerticalExtent(int index, Double defaultValue = default) => CameraVerticalExtent?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CameraHorizontalExtent { get; } + public Double[] CameraHorizontalExtent { get; } public Double GetCameraHorizontalExtent(int index, Double defaultValue = default) => CameraHorizontalExtent?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CameraFarDistance { get; } + public Double[] CameraFarDistance { get; } public Double GetCameraFarDistance(int index, Double defaultValue = default) => CameraFarDistance?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CameraNearDistance { get; } + public Double[] CameraNearDistance { get; } public Double GetCameraNearDistance(int index, Double defaultValue = default) => CameraNearDistance?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CameraTargetDistance { get; } + public Double[] CameraTargetDistance { get; } public Double GetCameraTargetDistance(int index, Double defaultValue = default) => CameraTargetDistance?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CameraRightOffset { get; } + public Double[] CameraRightOffset { get; } public Double GetCameraRightOffset(int index, Double defaultValue = default) => CameraRightOffset?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList CameraList { get; } + public Camera[] CameraList { get; } public Camera GetCamera(int n) { if (n < 0) return null; @@ -2929,48 +2929,48 @@ public Camera GetCamera(int n) public EntityTable MaterialEntityTable { get; } - public IList MaterialName { get; } + public String[] MaterialName { get; } public String GetMaterialName(int index, String defaultValue = "") => MaterialName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialMaterialCategory { get; } + public String[] MaterialMaterialCategory { get; } public String GetMaterialMaterialCategory(int index, String defaultValue = "") => MaterialMaterialCategory?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialColor_X { get; } + public Double[] MaterialColor_X { get; } public Double GetMaterialColor_X(int index, Double defaultValue = default) => MaterialColor_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialColor_Y { get; } + public Double[] MaterialColor_Y { get; } public Double GetMaterialColor_Y(int index, Double defaultValue = default) => MaterialColor_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialColor_Z { get; } + public Double[] MaterialColor_Z { get; } public Double GetMaterialColor_Z(int index, Double defaultValue = default) => MaterialColor_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialColorUvScaling_X { get; } + public Double[] MaterialColorUvScaling_X { get; } public Double GetMaterialColorUvScaling_X(int index, Double defaultValue = default) => MaterialColorUvScaling_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialColorUvScaling_Y { get; } + public Double[] MaterialColorUvScaling_Y { get; } public Double GetMaterialColorUvScaling_Y(int index, Double defaultValue = default) => MaterialColorUvScaling_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialColorUvOffset_X { get; } + public Double[] MaterialColorUvOffset_X { get; } public Double GetMaterialColorUvOffset_X(int index, Double defaultValue = default) => MaterialColorUvOffset_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialColorUvOffset_Y { get; } + public Double[] MaterialColorUvOffset_Y { get; } public Double GetMaterialColorUvOffset_Y(int index, Double defaultValue = default) => MaterialColorUvOffset_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialNormalUvScaling_X { get; } + public Double[] MaterialNormalUvScaling_X { get; } public Double GetMaterialNormalUvScaling_X(int index, Double defaultValue = default) => MaterialNormalUvScaling_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialNormalUvScaling_Y { get; } + public Double[] MaterialNormalUvScaling_Y { get; } public Double GetMaterialNormalUvScaling_Y(int index, Double defaultValue = default) => MaterialNormalUvScaling_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialNormalUvOffset_X { get; } + public Double[] MaterialNormalUvOffset_X { get; } public Double GetMaterialNormalUvOffset_X(int index, Double defaultValue = default) => MaterialNormalUvOffset_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialNormalUvOffset_Y { get; } + public Double[] MaterialNormalUvOffset_Y { get; } public Double GetMaterialNormalUvOffset_Y(int index, Double defaultValue = default) => MaterialNormalUvOffset_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialNormalAmount { get; } + public Double[] MaterialNormalAmount { get; } public Double GetMaterialNormalAmount(int index, Double defaultValue = default) => MaterialNormalAmount?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialGlossiness { get; } + public Double[] MaterialGlossiness { get; } public Double GetMaterialGlossiness(int index, Double defaultValue = default) => MaterialGlossiness?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialSmoothness { get; } + public Double[] MaterialSmoothness { get; } public Double GetMaterialSmoothness(int index, Double defaultValue = default) => MaterialSmoothness?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialTransparency { get; } + public Double[] MaterialTransparency { get; } public Double GetMaterialTransparency(int index, Double defaultValue = default) => MaterialTransparency?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialColorTextureFileIndex { get; } + public int[] MaterialColorTextureFileIndex { get; } public int GetMaterialColorTextureFileIndex(int index) => MaterialColorTextureFileIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList MaterialNormalTextureFileIndex { get; } + public int[] MaterialNormalTextureFileIndex { get; } public int GetMaterialNormalTextureFileIndex(int index) => MaterialNormalTextureFileIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList MaterialList { get; } + public Material[] MaterialList { get; } public Material GetMaterial(int n) { if (n < 0) return null; @@ -3005,18 +3005,18 @@ public Material GetMaterial(int n) public EntityTable MaterialInElementEntityTable { get; } - public IList MaterialInElementArea { get; } + public Double[] MaterialInElementArea { get; } public Double GetMaterialInElementArea(int index, Double defaultValue = default) => MaterialInElementArea?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialInElementVolume { get; } + public Double[] MaterialInElementVolume { get; } public Double GetMaterialInElementVolume(int index, Double defaultValue = default) => MaterialInElementVolume?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialInElementIsPaint { get; } + public Boolean[] MaterialInElementIsPaint { get; } public Boolean GetMaterialInElementIsPaint(int index, Boolean defaultValue = default) => MaterialInElementIsPaint?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList MaterialInElementMaterialIndex { get; } + public int[] MaterialInElementMaterialIndex { get; } public int GetMaterialInElementMaterialIndex(int index) => MaterialInElementMaterialIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList MaterialInElementList { get; } + public MaterialInElement[] MaterialInElementList { get; } public MaterialInElement GetMaterialInElement(int n) { if (n < 0) return null; @@ -3036,18 +3036,18 @@ public MaterialInElement GetMaterialInElement(int n) public EntityTable CompoundStructureLayerEntityTable { get; } - public IList CompoundStructureLayerOrderIndex { get; } + public Int32[] CompoundStructureLayerOrderIndex { get; } public Int32 GetCompoundStructureLayerOrderIndex(int index, Int32 defaultValue = default) => CompoundStructureLayerOrderIndex?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CompoundStructureLayerWidth { get; } + public Double[] CompoundStructureLayerWidth { get; } public Double GetCompoundStructureLayerWidth(int index, Double defaultValue = default) => CompoundStructureLayerWidth?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CompoundStructureLayerMaterialFunctionAssignment { get; } + public String[] CompoundStructureLayerMaterialFunctionAssignment { get; } public String GetCompoundStructureLayerMaterialFunctionAssignment(int index, String defaultValue = "") => CompoundStructureLayerMaterialFunctionAssignment?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList CompoundStructureLayerMaterialIndex { get; } + public int[] CompoundStructureLayerMaterialIndex { get; } public int GetCompoundStructureLayerMaterialIndex(int index) => CompoundStructureLayerMaterialIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList CompoundStructureLayerList { get; } + public CompoundStructureLayer[] CompoundStructureLayerList { get; } public CompoundStructureLayer GetCompoundStructureLayer(int n) { if (n < 0) return null; @@ -3067,12 +3067,12 @@ public CompoundStructureLayer GetCompoundStructureLayer(int n) public EntityTable CompoundStructureEntityTable { get; } - public IList CompoundStructureWidth { get; } + public Double[] CompoundStructureWidth { get; } public Double GetCompoundStructureWidth(int index, Double defaultValue = default) => CompoundStructureWidth?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList CompoundStructureList { get; } + public CompoundStructure[] CompoundStructureList { get; } public CompoundStructure GetCompoundStructure(int n) { if (n < 0) return null; @@ -3089,10 +3089,10 @@ public CompoundStructure GetCompoundStructure(int n) public EntityTable NodeEntityTable { get; } - public IList 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 IList NodeList { get; } + public Node[] NodeList { get; } public Node GetNode(int n) { if (n < 0) return null; @@ -3108,24 +3108,24 @@ public Node GetNode(int n) public EntityTable GeometryEntityTable { get; } - public IList 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 IList 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 IList 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 IList 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 IList 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 IList 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 IList GeometryVertexCount { get; } + public Int32[] GeometryVertexCount { get; } public Int32 GetGeometryVertexCount(int index, Int32 defaultValue = default) => GeometryVertexCount?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList GeometryList { get; } + public Geometry[] GeometryList { get; } public Geometry GetGeometry(int n) { if (n < 0) return null; @@ -3148,10 +3148,10 @@ public Geometry GetGeometry(int n) public EntityTable ShapeEntityTable { get; } - public IList 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 IList ShapeList { get; } + public Shape[] ShapeList { get; } public Shape GetShape(int n) { if (n < 0) return null; @@ -3167,10 +3167,10 @@ public Shape GetShape(int n) public EntityTable ShapeCollectionEntityTable { get; } - public IList 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 IList ShapeCollectionList { get; } + public ShapeCollection[] ShapeCollectionList { get; } public ShapeCollection GetShapeCollection(int n) { if (n < 0) return null; @@ -3186,12 +3186,12 @@ public ShapeCollection GetShapeCollection(int n) public EntityTable ShapeInShapeCollectionEntityTable { get; } - public IList ShapeInShapeCollectionShapeIndex { get; } + public int[] ShapeInShapeCollectionShapeIndex { get; } public int GetShapeInShapeCollectionShapeIndex(int index) => ShapeInShapeCollectionShapeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList ShapeInShapeCollectionList { get; } + public ShapeInShapeCollection[] ShapeInShapeCollectionList { get; } public ShapeInShapeCollection GetShapeInShapeCollection(int n) { if (n < 0) return null; @@ -3208,14 +3208,14 @@ public ShapeInShapeCollection GetShapeInShapeCollection(int n) public EntityTable SystemEntityTable { get; } - public IList SystemSystemType { get; } + public Int32[] SystemSystemType { get; } public Int32 GetSystemSystemType(int index, Int32 defaultValue = default) => SystemSystemType?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList SystemFamilyTypeIndex { get; } + public int[] SystemFamilyTypeIndex { get; } public int GetSystemFamilyTypeIndex(int index) => SystemFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList SystemList { get; } + public System[] SystemList { get; } public System GetSystem(int n) { if (n < 0) return null; @@ -3233,14 +3233,14 @@ public System GetSystem(int n) public EntityTable ElementInSystemEntityTable { get; } - public IList ElementInSystemRoles { get; } + public Int32[] ElementInSystemRoles { get; } public Int32 GetElementInSystemRoles(int index, Int32 defaultValue = default) => ElementInSystemRoles?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ElementInSystemSystemIndex { get; } + public int[] ElementInSystemSystemIndex { get; } public int GetElementInSystemSystemIndex(int index) => ElementInSystemSystemIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList ElementInSystemList { get; } + public ElementInSystem[] ElementInSystemList { get; } public ElementInSystem GetElementInSystem(int n) { if (n < 0) return null; @@ -3258,16 +3258,16 @@ public ElementInSystem GetElementInSystem(int n) public EntityTable WarningEntityTable { get; } - public IList WarningGuid { get; } + public String[] WarningGuid { get; } public String GetWarningGuid(int index, String defaultValue = "") => WarningGuid?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList WarningSeverity { get; } + public String[] WarningSeverity { get; } public String GetWarningSeverity(int index, String defaultValue = "") => WarningSeverity?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList WarningDescription { get; } + public String[] WarningDescription { get; } public String GetWarningDescription(int index, String defaultValue = "") => WarningDescription?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList WarningList { get; } + public Warning[] WarningList { get; } public Warning GetWarning(int n) { if (n < 0) return null; @@ -3286,12 +3286,12 @@ public Warning GetWarning(int n) public EntityTable ElementInWarningEntityTable { get; } - public IList ElementInWarningWarningIndex { get; } + public int[] ElementInWarningWarningIndex { get; } public int GetElementInWarningWarningIndex(int index) => ElementInWarningWarningIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList ElementInWarningList { get; } + public ElementInWarning[] ElementInWarningList { get; } public ElementInWarning GetElementInWarning(int n) { if (n < 0) return null; @@ -3308,24 +3308,24 @@ public ElementInWarning GetElementInWarning(int n) public EntityTable BasePointEntityTable { get; } - public IList BasePointIsSurveyPoint { get; } + public Boolean[] BasePointIsSurveyPoint { get; } public Boolean GetBasePointIsSurveyPoint(int index, Boolean defaultValue = default) => BasePointIsSurveyPoint?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BasePointPosition_X { get; } + public Double[] BasePointPosition_X { get; } public Double GetBasePointPosition_X(int index, Double defaultValue = default) => BasePointPosition_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BasePointPosition_Y { get; } + public Double[] BasePointPosition_Y { get; } public Double GetBasePointPosition_Y(int index, Double defaultValue = default) => BasePointPosition_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BasePointPosition_Z { get; } + public Double[] BasePointPosition_Z { get; } public Double GetBasePointPosition_Z(int index, Double defaultValue = default) => BasePointPosition_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BasePointSharedPosition_X { get; } + public Double[] BasePointSharedPosition_X { get; } public Double GetBasePointSharedPosition_X(int index, Double defaultValue = default) => BasePointSharedPosition_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BasePointSharedPosition_Y { get; } + public Double[] BasePointSharedPosition_Y { get; } public Double GetBasePointSharedPosition_Y(int index, Double defaultValue = default) => BasePointSharedPosition_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BasePointSharedPosition_Z { get; } + public Double[] BasePointSharedPosition_Z { get; } public Double GetBasePointSharedPosition_Z(int index, Double defaultValue = default) => BasePointSharedPosition_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList BasePointList { get; } + public BasePoint[] BasePointList { get; } public BasePoint GetBasePoint(int n) { if (n < 0) return null; @@ -3348,18 +3348,18 @@ public BasePoint GetBasePoint(int n) public EntityTable PhaseFilterEntityTable { get; } - public IList PhaseFilterNew { get; } + public Int32[] PhaseFilterNew { get; } public Int32 GetPhaseFilterNew(int index, Int32 defaultValue = default) => PhaseFilterNew?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList PhaseFilterExisting { get; } + public Int32[] PhaseFilterExisting { get; } public Int32 GetPhaseFilterExisting(int index, Int32 defaultValue = default) => PhaseFilterExisting?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList PhaseFilterDemolished { get; } + public Int32[] PhaseFilterDemolished { get; } public Int32 GetPhaseFilterDemolished(int index, Int32 defaultValue = default) => PhaseFilterDemolished?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList PhaseFilterTemporary { get; } + public Int32[] PhaseFilterTemporary { get; } public Int32 GetPhaseFilterTemporary(int index, Int32 defaultValue = default) => PhaseFilterTemporary?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList PhaseFilterList { get; } + public PhaseFilter[] PhaseFilterList { get; } public PhaseFilter GetPhaseFilter(int n) { if (n < 0) return null; @@ -3379,38 +3379,38 @@ public PhaseFilter GetPhaseFilter(int n) public EntityTable GridEntityTable { get; } - public IList GridStartPoint_X { get; } + public Double[] GridStartPoint_X { get; } public Double GetGridStartPoint_X(int index, Double defaultValue = default) => GridStartPoint_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList GridStartPoint_Y { get; } + public Double[] GridStartPoint_Y { get; } public Double GetGridStartPoint_Y(int index, Double defaultValue = default) => GridStartPoint_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList GridStartPoint_Z { get; } + public Double[] GridStartPoint_Z { get; } public Double GetGridStartPoint_Z(int index, Double defaultValue = default) => GridStartPoint_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList GridEndPoint_X { get; } + public Double[] GridEndPoint_X { get; } public Double GetGridEndPoint_X(int index, Double defaultValue = default) => GridEndPoint_X?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList GridEndPoint_Y { get; } + public Double[] GridEndPoint_Y { get; } public Double GetGridEndPoint_Y(int index, Double defaultValue = default) => GridEndPoint_Y?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList GridEndPoint_Z { get; } + public Double[] GridEndPoint_Z { get; } public Double GetGridEndPoint_Z(int index, Double defaultValue = default) => GridEndPoint_Z?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList GridIsCurved { get; } + public Boolean[] GridIsCurved { get; } public Boolean GetGridIsCurved(int index, Boolean defaultValue = default) => GridIsCurved?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList 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 IList 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 IList 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 IList 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 IList 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 IList GridFamilyTypeIndex { get; } + public int[] GridFamilyTypeIndex { get; } public int GetGridFamilyTypeIndex(int index) => GridFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList GridList { get; } + public Grid[] GridList { get; } public Grid GetGrid(int n) { if (n < 0) return null; @@ -3440,20 +3440,20 @@ public Grid GetGrid(int n) public EntityTable AreaEntityTable { get; } - public IList AreaValue { get; } + public Double[] AreaValue { get; } public Double GetAreaValue(int index, Double defaultValue = default) => AreaValue?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList AreaPerimeter { get; } + public Double[] AreaPerimeter { get; } public Double GetAreaPerimeter(int index, Double defaultValue = default) => AreaPerimeter?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList AreaNumber { get; } + public String[] AreaNumber { get; } public String GetAreaNumber(int index, String defaultValue = "") => AreaNumber?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList AreaIsGrossInterior { get; } + public Boolean[] AreaIsGrossInterior { get; } public Boolean GetAreaIsGrossInterior(int index, Boolean defaultValue = default) => AreaIsGrossInterior?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList AreaAreaSchemeIndex { get; } + public int[] AreaAreaSchemeIndex { get; } public int GetAreaAreaSchemeIndex(int index) => AreaAreaSchemeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList AreaList { get; } + public Area[] AreaList { get; } public Area GetArea(int n) { if (n < 0) return null; @@ -3474,12 +3474,12 @@ public Area GetArea(int n) public EntityTable AreaSchemeEntityTable { get; } - public IList AreaSchemeIsGrossBuildingArea { get; } + public Boolean[] AreaSchemeIsGrossBuildingArea { get; } public Boolean GetAreaSchemeIsGrossBuildingArea(int index, Boolean defaultValue = default) => AreaSchemeIsGrossBuildingArea?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList AreaSchemeList { get; } + public AreaScheme[] AreaSchemeList { get; } public AreaScheme GetAreaScheme(int n) { if (n < 0) return null; @@ -3496,10 +3496,10 @@ public AreaScheme GetAreaScheme(int n) public EntityTable ScheduleEntityTable { get; } - public IList 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 IList ScheduleList { get; } + public Schedule[] ScheduleList { get; } public Schedule GetSchedule(int n) { if (n < 0) return null; @@ -3515,14 +3515,14 @@ public Schedule GetSchedule(int n) public EntityTable ScheduleColumnEntityTable { get; } - public IList ScheduleColumnName { get; } + public String[] ScheduleColumnName { get; } public String GetScheduleColumnName(int index, String defaultValue = "") => ScheduleColumnName?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ScheduleColumnColumnIndex { get; } + public Int32[] ScheduleColumnColumnIndex { get; } public Int32 GetScheduleColumnColumnIndex(int index, Int32 defaultValue = default) => ScheduleColumnColumnIndex?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList ScheduleColumnList { get; } + public ScheduleColumn[] ScheduleColumnList { get; } public ScheduleColumn GetScheduleColumn(int n) { if (n < 0) return null; @@ -3540,14 +3540,14 @@ public ScheduleColumn GetScheduleColumn(int n) public EntityTable ScheduleCellEntityTable { get; } - public IList ScheduleCellValue { get; } + public String[] ScheduleCellValue { get; } public String GetScheduleCellValue(int index, String defaultValue = "") => ScheduleCellValue?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList ScheduleCellRowIndex { get; } + public Int32[] ScheduleCellRowIndex { get; } public Int32 GetScheduleCellRowIndex(int index, Int32 defaultValue = default) => ScheduleCellRowIndex?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList ScheduleCellList { get; } + public ScheduleCell[] ScheduleCellList { get; } public ScheduleCell GetScheduleCell(int n) { if (n < 0) return null; @@ -3565,10 +3565,10 @@ public ScheduleCell GetScheduleCell(int n) public EntityTable ViewSheetSetEntityTable { get; } - public IList 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 IList ViewSheetSetList { get; } + public ViewSheetSet[] ViewSheetSetList { get; } public ViewSheetSet GetViewSheetSet(int n) { if (n < 0) return null; @@ -3584,12 +3584,12 @@ public ViewSheetSet GetViewSheetSet(int n) public EntityTable ViewSheetEntityTable { get; } - public IList ViewSheetFamilyTypeIndex { get; } + public int[] ViewSheetFamilyTypeIndex { get; } public int GetViewSheetFamilyTypeIndex(int index) => ViewSheetFamilyTypeIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList ViewSheetList { get; } + public ViewSheet[] ViewSheetList { get; } public ViewSheet GetViewSheet(int n) { if (n < 0) return null; @@ -3606,12 +3606,12 @@ public ViewSheet GetViewSheet(int n) public EntityTable ViewSheetInViewSheetSetEntityTable { get; } - public IList ViewSheetInViewSheetSetViewSheetIndex { get; } + public int[] ViewSheetInViewSheetSetViewSheetIndex { get; } public int GetViewSheetInViewSheetSetViewSheetIndex(int index) => ViewSheetInViewSheetSetViewSheetIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList ViewSheetInViewSheetSetList { get; } + public ViewSheetInViewSheetSet[] ViewSheetInViewSheetSetList { get; } public ViewSheetInViewSheetSet GetViewSheetInViewSheetSet(int n) { if (n < 0) return null; @@ -3628,12 +3628,12 @@ public ViewSheetInViewSheetSet GetViewSheetInViewSheetSet(int n) public EntityTable ViewInViewSheetSetEntityTable { get; } - public IList ViewInViewSheetSetViewIndex { get; } + public int[] ViewInViewSheetSetViewIndex { get; } public int GetViewInViewSheetSetViewIndex(int index) => ViewInViewSheetSetViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList ViewInViewSheetSetList { get; } + public ViewInViewSheetSet[] ViewInViewSheetSetList { get; } public ViewInViewSheetSet GetViewInViewSheetSet(int n) { if (n < 0) return null; @@ -3650,12 +3650,12 @@ public ViewInViewSheetSet GetViewInViewSheetSet(int n) public EntityTable ViewInViewSheetEntityTable { get; } - public IList ViewInViewSheetViewIndex { get; } + public int[] ViewInViewSheetViewIndex { get; } public int GetViewInViewSheetViewIndex(int index) => ViewInViewSheetViewIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList ViewInViewSheetList { get; } + public ViewInViewSheet[] ViewInViewSheetList { get; } public ViewInViewSheet GetViewInViewSheet(int n) { if (n < 0) return null; @@ -3672,20 +3672,20 @@ public ViewInViewSheet GetViewInViewSheet(int n) public EntityTable SiteEntityTable { get; } - public IList SiteLatitude { get; } + public Double[] SiteLatitude { get; } public Double GetSiteLatitude(int index, Double defaultValue = default) => SiteLatitude?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList SiteLongitude { get; } + public Double[] SiteLongitude { get; } public Double GetSiteLongitude(int index, Double defaultValue = default) => SiteLongitude?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList SiteAddress { get; } + public String[] SiteAddress { get; } public String GetSiteAddress(int index, String defaultValue = "") => SiteAddress?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList SiteElevation { get; } + public Double[] SiteElevation { get; } public Double GetSiteElevation(int index, Double defaultValue = default) => SiteElevation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList SiteNumber { get; } + public String[] SiteNumber { get; } public String GetSiteNumber(int index, String defaultValue = "") => SiteNumber?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList 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 IList SiteList { get; } + public Site[] SiteList { get; } public Site GetSite(int n) { if (n < 0) return null; @@ -3706,18 +3706,18 @@ public Site GetSite(int n) public EntityTable BuildingEntityTable { get; } - public IList BuildingElevation { get; } + public Double[] BuildingElevation { get; } public Double GetBuildingElevation(int index, Double defaultValue = default) => BuildingElevation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BuildingTerrainElevation { get; } + public Double[] BuildingTerrainElevation { get; } public Double GetBuildingTerrainElevation(int index, Double defaultValue = default) => BuildingTerrainElevation?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BuildingAddress { get; } + public String[] BuildingAddress { get; } public String GetBuildingAddress(int index, String defaultValue = "") => BuildingAddress?.ElementAtOrDefault(index, defaultValue) ?? defaultValue; - public IList BuildingSiteIndex { get; } + public int[] BuildingSiteIndex { get; } public int GetBuildingSiteIndex(int index) => BuildingSiteIndex?.ElementAtOrDefault(index, EntityRelation.None) ?? EntityRelation.None; - public IList 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 IList BuildingList { get; } + public Building[] BuildingList { get; } public Building GetBuilding(int n) { if (n < 0) return null; @@ -4239,60 +4239,60 @@ public DocumentModel(Document d, bool inParallel = true) 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 923dc0fd..14d5d24c 100644 --- a/src/cs/vim/Vim.Format/ObjectModel/Validation.cs +++ b/src/cs/vim/Vim.Format/ObjectModel/Validation.cs @@ -4,7 +4,7 @@ using System.Reflection; using System.Threading.Tasks; using Vim.Math3d; -using Vim.G3d; +using Vim.Util; namespace Vim.Format.ObjectModel { @@ -52,7 +52,7 @@ public ObjectModelValidationException(string message) : base(message) { } public static class Validation { - private static void ValidateBimDocument(this DocumentModel dm, ObjectModelValidationOptions validationOptions) + public static void ValidateBimDocument(this DocumentModel dm, ObjectModelValidationOptions validationOptions) { // There is at least one BimDocument in the document model. if (dm.NumBimDocument == 0 && validationOptions.BimDocumentMustExist) @@ -78,14 +78,14 @@ private static void ValidateBimDocument(this DocumentModel dm, ObjectModelValida } } - private static void ValidateCompoundStructureLayer(this CompoundStructureLayer layer) + public static void ValidateCompoundStructureLayer(this CompoundStructureLayer layer) { // All CompoundLayers have a CompoundStructure if (layer.CompoundStructure == null) throw new ObjectModelValidationException($"{nameof(CompoundStructureLayer)} {layer.Index} has null {nameof(CompoundStructure)}"); } - private static void ValidateCompoundStructures(this DocumentModel dm) + public static void ValidateCompoundStructures(this DocumentModel dm) { var cslArray = dm.CompoundStructureLayerList.ToArray(); @@ -136,7 +136,7 @@ private static void ValidateCompoundStructures(this DocumentModel dm) throw new ObjectModelValidationException($"A {nameof(CompoundStructure)} must be referenced by exactly one {nameof(FamilyType)}."); } - private static void ValidateAssets(this DocumentModel dm) + public static void ValidateAssets(this DocumentModel dm) { // Validate that the assets contained in the buffers matches the assets entity table. var assetBuffers = dm.Document.Assets; @@ -148,7 +148,7 @@ private static void ValidateAssets(this DocumentModel dm) } } - private static void ValidateParameters(this DocumentModel dm) + public static void ValidateParameters(this DocumentModel dm) { Parallel.ForEach(dm.ParameterList, p => { @@ -169,7 +169,7 @@ private static void ValidateParameters(this DocumentModel dm) } } - private static void ValidatePhases(this DocumentModel dm) + public static void ValidatePhases(this DocumentModel dm) { // Validate the phase order information. var poArray = dm.PhaseOrderInBimDocumentList.ToArray(); @@ -207,7 +207,7 @@ private static void ValidatePhases(this DocumentModel dm) /// /// Generic storage key check; ensures that the keys only appear once in the keySet. /// - private static void ValidateStorageKeys(IEnumerable storageKeyEntities) + public static void ValidateStorageKeys(IEnumerable storageKeyEntities) { var keySet = new HashSet(); foreach (var entity in storageKeyEntities) @@ -221,7 +221,7 @@ private static void ValidateStorageKeys(IEnumerable storageKeyEntit /// /// Validate that the entities which inherit from IStorageKey all have unique storage key values. /// - private static void ValidateStorageKeys(this DocumentModel dm) + public static void ValidateStorageKeys(this DocumentModel dm) { // ReSharper disable once SuspiciousTypeConversion.Global var storageKeyEntityLists = dm.AllEntities @@ -232,7 +232,7 @@ private static void ValidateStorageKeys(this DocumentModel dm) } } - private static void ValidateMaterials(this DocumentModel dm) + public static void ValidateMaterials(this DocumentModel dm) { void ValidateDomain(string label, double value, double lowerInclusive, double upperInclusive, int index) { @@ -264,8 +264,8 @@ void ValidateDVector3Domain(string label, DVector3 value, DVector3 lowerInclusiv } } - private static Dictionary> GetViewToElementsMap( - this IList elementInViewList) + public static Dictionary> GetViewToElementsMap( + this ElementInView[] elementInViewList) => elementInViewList .Select(eiv => (viewIndex: eiv._View?.Index ?? -1, elementIndex: eiv._Element?.Index ?? -1)) .GroupBy(t => t.viewIndex) @@ -273,9 +273,9 @@ private static Dictionary> GetViewToElementsMap( g => g.Key, g => new HashSet(g.Select(t => t.elementIndex))); - private static void ValidateShapesInView(this DocumentModel dm) + 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) @@ -293,7 +293,7 @@ private static void ValidateShapesInView(this DocumentModel dm) } } - private static void ValidateEntitiesWithElement(this DocumentModel dm) + public static void ValidateEntitiesWithElement(this DocumentModel dm) { var entityWithElementTypes = new HashSet(ObjectModelReflection.GetEntityTypes() .Where(t => t.GetCustomAttributes(typeof(G3dAttributeReferenceAttribute)).Count() == 0)); @@ -302,8 +302,8 @@ private static void ValidateEntitiesWithElement(this DocumentModel dm) Parallel.ForEach(entityWithElementTypes, entityWithElementType => { - var elementIndices = (IList) dm.GetPropertyValue(entityWithElementType.Name + "ElementIndex"); - for (var i = 0; i < elementIndices.Count; ++i) + var elementIndices = ((int[])dm.GetPropertyValue(entityWithElementType.Name + "ElementIndex")).ToArray(); + for (var i = 0; i < elementIndices.Length; ++i) { var elementIndex = elementIndices[i]; if (elementIndex < 0) @@ -314,7 +314,7 @@ private static void ValidateEntitiesWithElement(this DocumentModel dm) }); } - private static void ValidateElementInSystem(this DocumentModel dm) + public static void ValidateElementInSystem(this DocumentModel dm) { foreach (var eis in dm.ElementInSystemList) { 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 55be0d2e..c817e6e4 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/Validation.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -7,8 +6,8 @@ using Vim.BFastLib; using Vim.Format.Geometry; using Vim.Format.ObjectModel; -using Vim.Util; using Vim.Math3d; +using Vim.Util; namespace Vim.Format.SceneBuilder { @@ -19,28 +18,16 @@ public class VimValidationOptions public static class Validation { - private class VimValidationException : Exception + public class VimValidationException : Exception { public VimValidationException(string message) : base(message) { } } - - private 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) - g.Validate(); - } - - private 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,29 +43,27 @@ private static void ValidateDocumentModelToG3dInvariants(this VimScene vim) { var (type, attrs) = tuple; var propertyName = type.Name + "List"; - - if (dm.GetPropertyValue(propertyName) is IList 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})"); } } } @@ -95,18 +80,17 @@ private static void ValidateDocumentModelToG3dInvariants(this VimScene vim) } } - private static void ValidateNodes(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})"); } - private static void ValidateShapes(this VimScene vim) + 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(VimShapeNext)} ({vim.GetShapeCount()}) does not match the number of shape entities ({vim.DocumentModel.NumShape})"); void ValidateColorDomain(string label, Vector4 value, Vector4 lowerInclusive, Vector4 upperInclusive, int index) { @@ -123,12 +107,13 @@ void ValidateColorDomain(string label, Vector4 value, Vector4 lowerInclusive, Ve } } - Parallel.For(0, numShapes, shapeIndex => + Parallel.For(0, vim.GetShapeCount(), shapeIndex => { var shape = shapes[shapeIndex]; - if (shape.ElementIndex < 0) - throw new VimValidationException($"{nameof(Element)} is null for {nameof(VimShape)} {shape.ShapeIndex}"); - ValidateColorDomain($"{nameof(VimShape)} color", shape.Color, Vector4.Zero, Vector4.One, shape.ShapeIndex); + var element = vim.DocumentModel.GetShapeElementIndex(shapeIndex); + if (element < 0) + throw new VimValidationException($"{nameof(Element)} is null for {nameof(VimShapeNext)} {shape.Index}"); + ValidateColorDomain($"{nameof(VimShapeNext)} color", shape.Color, Vector4.Zero, Vector4.One, shape.Index); }); } @@ -138,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.Geometry.MeshCount; ++i) - { - if (!db.Geometry.GetMesh(i).IsEquivalentTo(vimGeoBuilders[i])) - throw new VimValidationException($"{nameof(DocumentBuilder)} mesh {i} is not equivalent to {nameof(VimScene)} mesh {i}"); - - if (!db.Geometry.GetMesh(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()); @@ -171,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) + foreach (var et in vim.Document.TableNames) { if (!tableNames.Contains(et)) throw new VimValidationException($"{nameof(DocumentBuilder)} does not contain table name {et} from {nameof(VimScene)}"); diff --git a/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs b/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs index c4d25b89..faa410b4 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/VimScene.cs @@ -3,14 +3,14 @@ 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.Math3d; +using Vim.Util; using IVimSceneProgress = System.IProgress<(string, double)>; -using Vim.BFastLib; namespace Vim { @@ -19,7 +19,7 @@ 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. @@ -49,20 +49,20 @@ public static VimScene LoadVim(string f, LoadOptions loadOptions, IVimSceneProgr => 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(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 IList Meshes { get; private set; } - public IList Nodes { get; private set; } - public IList VimNodes { get; private set; } - public IList VimShapes { get; private set; } - public IList Materials { get; private set; } + public VimSceneNode[] Nodes { get; private set; } + + public VimMesh[] Meshes { get; private set; } + public VimShapeNext[] Shapes { get; private set; } + public VimMaterialNext[] Materials { get; private set; } public SerializableDocument _SerializableDocument { get; } public Document Document { get; private set; } @@ -71,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]; - - private 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; @@ -188,61 +192,48 @@ private IStep[] GetInitSteps(bool inParallel) private void CreateMeshes(bool inParallel) { - if (_SerializableDocument.Geometry == null) + if (_SerializableDocument.GeometryNext == null) { return; } - var srcGeo = _SerializableDocument.Geometry; - var tmp = srcGeo?.Meshes.Select(ToIMesh); - Meshes = (tmp == null) - ? Array.Empty() - : inParallel - ? tmp.AsParallel().ToArray() - : tmp.ToArray(); + Meshes = VimMesh.GetAllMeshes(_SerializableDocument.GeometryNext).ToArray(); } private void CreateShapes(bool inParallel) { - if (_SerializableDocument.Geometry == null) + if (_SerializableDocument.GeometryNext == null) { return; } - - var r = _SerializableDocument.Geometry.Shapes.Select((s, i) => new VimShape(this, i)); - VimShapes = inParallel ? r.AsParallel().ToArray() : r.ToArray(); + Shapes = VimShapeNext.FromG3d(_SerializableDocument.GeometryNext).ToArray(); } private void CreateScene(bool inParallel) { - if (_SerializableDocument.Geometry == null) + if (_SerializableDocument.GeometryNext == null) { return; } - VimNodes = CreateVimSceneNodes(this, _SerializableDocument.Geometry, inParallel); - Nodes = VimNodes.Select(n => n as ISceneNode).ToArray(); + Nodes = CreateVimSceneNodes(this, _SerializableDocument.GeometryNext, inParallel); } private void CreateMaterials(bool inParallel) { - if (_SerializableDocument.Geometry == null) + if (_SerializableDocument.GeometryNext == null) { return; } - - var query = _SerializableDocument.Geometry.Materials.Select(m => new VimMaterial(m) as IMaterial); - Materials = inParallel ? query.AsParallel().ToArray() : query.ToArray(); + Materials = VimMaterialNext.FromG3d(_SerializableDocument.GeometryNext).ToArray(); } - public static IList 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.AsParallel().ToArray() : r.ToArray(); + return g3d.InstanceTransforms.Select((_, i) => + new VimSceneNode(scene, i, g3d.InstanceMeshes[i], GetMatrix(i))).ToArray(); } public void Save(string filePath) @@ -250,12 +241,12 @@ public void Save(string 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).AsParallel().ToArray(); + Meshes = Meshes.Select(meshTransform).ToArray(); if (nodeTransform != null) - VimNodes = VimNodes.Select(nodeTransform).AsParallel().ToArray(); + 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 635d27de..962916a6 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/VimSceneHelpers.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/VimSceneHelpers.cs @@ -155,14 +155,23 @@ 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 IList MaterialColors(this VimScene scene) => scene.DocumentModel.MaterialList.Select(GetDiffuseColor).ToArray(); + 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); public static IEnumerable<(string assetBufferName, FileInfo assetFileInfo)> ExtractAssets(this VimScene vim, DirectoryInfo directory) => vim.Document.ExtractAssets(directory); + public static FileInfo ExtractAsset(this VimScene vim, string assetBufferName, FileInfo fileInfo) + => vim.Document.ExtractAsset(assetBufferName, fileInfo); + + public static ElementInfo GetElementInfo(this VimScene vim, int elementIndex) + => vim.DocumentModel.GetElementInfo(elementIndex); + public static ElementInfo GetElementInfo(this VimScene vim, Element element) => vim.DocumentModel.GetElementInfo(element); + + public static VimSchema GetVimSchema(this VimScene vim) + => VimSchema.Create(vim._SerializableDocument); } } diff --git a/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs b/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs index 27d4ecb0..6098cc22 100644 --- a/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs +++ b/src/cs/vim/Vim.Format/SceneBuilder/VimSceneNode.cs @@ -1,54 +1,71 @@ -using Vim.Format.Geometry; +using System; +using System.Collections.Generic; +using System.Linq; +using Vim.Format.Geometry; using Vim.Format.ObjectModel; using Vim.G3d; -using Vim.LinqArray; using Vim.Math3d; namespace Vim { - public sealed class VimSceneNode : ElementInfo, ISceneNode, ITransformable3D + public sealed class VimSceneNode : ElementInfo, ITransformable3D { public VimSceneNode(VimScene scene, int nodeIndex, int geometryIndex, Matrix4x4 transform) : base(scene.DocumentModel, scene.DocumentModel.GetNodeElementIndex(nodeIndex)) { VimIndex = scene.VimIndex; - _Scene = scene; + Scene = scene; Transform = transform; MeshIndex = geometryIndex; NodeIndex = nodeIndex; } - public VimScene _Scene { get; } + public VimScene Scene { get; } - public IScene Scene => _Scene; public int Id => NodeIndex; public Matrix4x4 Transform { get; } public InstanceFlags InstanceFlags - => (InstanceFlags)_Scene.Document.Geometry.InstanceFlags.ElementAtOrDefault(NodeIndex); + => (InstanceFlags) Scene.Document.GeometryNext.InstanceFlags.ElementAtOrDefault(NodeIndex); public bool HideByDefault - => (InstanceFlags & InstanceFlags.Hidden) == InstanceFlags.Hidden; + => Scene.Document.GeometryNext.InstanceHasFlag(NodeIndex, InstanceFlags.Hidden); public int VimIndex { get; } = -1; public int NodeIndex { get; } = -1; - public IMesh GetMesh() - => _Scene.Meshes.ElementAtOrDefault(MeshIndex); + public VimMesh GetMesh() + => Scene.Meshes.SafeGet(MeshIndex); public int MeshIndex { get; } public bool HasMesh => MeshIndex != -1; - public Node NodeModel => _Scene.DocumentModel.GetNode(Id); - public Geometry GeometryModel => _Scene.DocumentModel.GetGeometry(MeshIndex); + public Node NodeModel => Scene.DocumentModel.GetNode(Id); + public Geometry GeometryModel => Scene.DocumentModel.GetGeometry(MeshIndex); // TODO: I think this should be "IEnumerable" in the interface - public ISceneNode Parent => null; + public 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 dfbd4c71..00000000 --- a/src/cs/vim/Vim.Format/SceneBuilder/VimShape.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; -using Vim.Format.ObjectModel; -using Vim.G3d; -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 IList Vertices => G3dShape.Vertices; - public Vector4 Color => G3dShape.Color; - public float Width => G3dShape.Width; - - public VimShape(VimScene scene, int shapeIndex) - : base(scene.DocumentModel, scene.DocumentModel.GetShapeElementIndex(shapeIndex)) - { - Scene = scene; - ShapeIndex = shapeIndex; - } - } -} diff --git a/src/cs/vim/Vim.Format/Vim.Format.csproj b/src/cs/vim/Vim.Format/Vim.Format.csproj index 5f258aac..db3e19f8 100644 --- a/src/cs/vim/Vim.Format/Vim.Format.csproj +++ b/src/cs/vim/Vim.Format/Vim.Format.csproj @@ -10,7 +10,7 @@ GitHub true MIT - 1.1.0 + 1.0.2 true true true @@ -18,12 +18,9 @@ - - - - - - + + +