diff --git a/Speckle.DoubleNumerics.Tests/ConversionExtensionsTests.cs b/Speckle.DoubleNumerics.Tests/ConversionExtensionsTests.cs new file mode 100644 index 0000000..06a46be --- /dev/null +++ b/Speckle.DoubleNumerics.Tests/ConversionExtensionsTests.cs @@ -0,0 +1,222 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.Intrinsics; +using Xunit; + +namespace Speckle.DoubleNumerics.Tests; + +public class ConversionExtensionsTests +{ + [Fact] + public void PlaneAsVector4Test() + { + var plane = new Plane(1, 2, 3, 4); + var vector4 = plane.AsVector4(); + Assert.Equal(plane.Normal.X, vector4.X); + Assert.Equal(plane.Normal.Y, vector4.Y); + Assert.Equal(plane.Normal.Z, vector4.Z); + Assert.Equal(plane.D, vector4.W); + } + + [Fact] + public void PlaneAsVector256Test() + { + var plane = new Plane(1, 2, 3, 4); + var vector256 = plane.AsVector256(); + Assert.Equal(plane.Normal.X, vector256[0]); + Assert.Equal(plane.Normal.Y, vector256[1]); + Assert.Equal(plane.Normal.Z, vector256[2]); + Assert.Equal(plane.D, vector256[3]); + } + + [Fact] + public void QuaternionAsVector4Test() + { + var quaternion = new Quaternion(1, 2, 3, 4); + var vector4 = quaternion.AsVector4(); + Assert.Equal(quaternion.X, vector4.X); + Assert.Equal(quaternion.Y, vector4.Y); + Assert.Equal(quaternion.Z, vector4.Z); + Assert.Equal(quaternion.W, vector4.W); + } + + [Fact] + public void QuaternionAsVector256Test() + { + var quaternion = new Quaternion(1, 2, 3, 4); + var vector256 = quaternion.AsVector256(); + Assert.Equal(quaternion.X, vector256[0]); + Assert.Equal(quaternion.Y, vector256[1]); + Assert.Equal(quaternion.Z, vector256[2]); + Assert.Equal(quaternion.W, vector256[3]); + } + + [Fact] + public void Vector2AsVector3Test() + { + var vector2 = new Vector2(1, 2); + var vector3 = vector2.AsVector3(); + Assert.Equal(vector2.X, vector3.X); + Assert.Equal(vector2.Y, vector3.Y); + Assert.Equal(0, vector3.Z); + } + + [Fact] + public void Vector2AsVector4Test() + { + var vector2 = new Vector2(1, 2); + var vector4 = vector2.AsVector4(); + Assert.Equal(vector2.X, vector4.X); + Assert.Equal(vector2.Y, vector4.Y); + Assert.Equal(0, vector4.Z); + Assert.Equal(0, vector4.W); + } + + [Fact] + public void Vector2AsVector256Test() + { + var vector2 = new Vector2(1, 2); + var vector256 = vector2.AsVector256(); + Assert.Equal(vector2.X, vector256[0]); + Assert.Equal(vector2.Y, vector256[1]); + Assert.Equal(0, vector256[2]); + Assert.Equal(0, vector256[3]); + } + + [Fact] + public void Vector3AsVector2Test() + { + var vector3 = new Vector3(1, 2, 3); + var vector2 = vector3.AsVector2(); + Assert.Equal(vector3.X, vector2.X); + Assert.Equal(vector3.Y, vector2.Y); + } + + [Fact] + public void Vector3AsVector4Test() + { + var vector3 = new Vector3(1, 2, 3); + var vector4 = vector3.AsVector4(); + Assert.Equal(vector3.X, vector4.X); + Assert.Equal(vector3.Y, vector4.Y); + Assert.Equal(vector3.Z, vector4.Z); + Assert.Equal(0, vector4.W); + } + + [Fact] + public void Vector3AsVector256Test() + { + var vector3 = new Vector3(1, 2, 3); + var vector256 = vector3.AsVector256(); + Assert.Equal(vector3.X, vector256[0]); + Assert.Equal(vector3.Y, vector256[1]); + Assert.Equal(vector3.Z, vector256[2]); + Assert.Equal(0, vector256[3]); + } + + [Fact] + public void Vector4AsQuaternionTest() + { + var vector4 = new Vector4(1, 2, 3, 4); + var quaternion = vector4.AsQuaternion(); + Assert.Equal(vector4.X, quaternion.X); + Assert.Equal(vector4.Y, quaternion.Y); + Assert.Equal(vector4.Z, quaternion.Z); + Assert.Equal(vector4.W, quaternion.W); + } + + [Fact] + public void Vector4AsPlaneTest() + { + var vector4 = new Vector4(1, 2, 3, 4); + var plane = vector4.AsPlane(); + Assert.Equal(vector4.X, plane.Normal.X); + Assert.Equal(vector4.Y, plane.Normal.Y); + Assert.Equal(vector4.Z, plane.Normal.Z); + Assert.Equal(vector4.W, plane.D); + } + + [Fact] + public void Vector4AsVector2Test() + { + var vector4 = new Vector4(1, 2, 3, 4); + var vector2 = vector4.AsVector2(); + Assert.Equal(vector4.X, vector2.X); + Assert.Equal(vector4.Y, vector2.Y); + } + + [Fact] + public void Vector4AsVector3Test() + { + var vector4 = new Vector4(1, 2, 3, 4); + var vector3 = vector4.AsVector3(); + Assert.Equal(vector4.X, vector3.X); + Assert.Equal(vector4.Y, vector3.Y); + Assert.Equal(vector4.Z, vector3.Z); + } + + [Fact] + public void Vector4AsVector256Test() + { + var vector4 = new Vector4(1, 2, 3, 4); + var vector256 = vector4.AsVector256(); + Assert.Equal(vector4.X, vector256[0]); + Assert.Equal(vector4.Y, vector256[1]); + Assert.Equal(vector4.Z, vector256[2]); + Assert.Equal(vector4.W, vector256[3]); + } + + [Fact] + public void Vector256AsPlaneTest() + { + var vector256 = Vector256.Create(1d, 2d, 3d, 4d); + var plane = vector256.AsPlane(); + Assert.Equal(vector256[0], plane.Normal.X); + Assert.Equal(vector256[1], plane.Normal.Y); + Assert.Equal(vector256[2], plane.Normal.Z); + Assert.Equal(vector256[3], plane.D); + } + + [Fact] + public void Vector256AsQuaternionTest() + { + var vector256 = Vector256.Create(1d, 2d, 3d, 4d); + var quaternion = vector256.AsQuaternion(); + Assert.Equal(vector256[0], quaternion.X); + Assert.Equal(vector256[1], quaternion.Y); + Assert.Equal(vector256[2], quaternion.Z); + Assert.Equal(vector256[3], quaternion.W); + } + + [Fact] + public void Vector256AsVector2Test() + { + var vector256 = Vector256.Create(1d, 2d, 0d, 0d); + var vector2 = vector256.AsVector2(); + Assert.Equal(vector256[0], vector2.X); + Assert.Equal(vector256[1], vector2.Y); + } + + [Fact] + public void Vector256AsVector3Test() + { + var vector256 = Vector256.Create(1d, 2d, 3d, 0d); + var vector3 = vector256.AsVector3(); + Assert.Equal(vector256[0], vector3.X); + Assert.Equal(vector256[1], vector3.Y); + Assert.Equal(vector256[2], vector3.Z); + } + + [Fact] + public void Vector256AsVector4Test() + { + var vector256 = Vector256.Create(1d, 2d, 3d, 4d); + var vector4 = vector256.AsVector4(); + Assert.Equal(vector256[0], vector4.X); + Assert.Equal(vector256[1], vector4.Y); + Assert.Equal(vector256[2], vector4.Z); + Assert.Equal(vector256[3], vector4.W); + } +} diff --git a/Speckle.DoubleNumerics.Tests/ConverterTests.cs b/Speckle.DoubleNumerics.Tests/ConverterTests.cs new file mode 100644 index 0000000..2f7bb48 --- /dev/null +++ b/Speckle.DoubleNumerics.Tests/ConverterTests.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text.Json; +using Xunit; + +namespace Speckle.DoubleNumerics.Tests; + +public class ConverterTests +{ + [Fact] + public void PlaneConverterTest() => AssertRoundTrip(new Plane(1, 2, 3, 4)); + + [Fact] + public void Vector2ConverterTest() => AssertRoundTrip(new Vector2(1, 2)); + + [Fact] + public void Vector3ConverterTest() => AssertRoundTrip(new Vector3(1, 2, 3)); + + [Fact] + public void Vector4ConverterTest() => AssertRoundTrip(new Vector4(1, 2, 3, 4)); + + [Fact] + public void QuaternionConverterTest() => AssertRoundTrip(new Quaternion(1, 2, 3, 4)); + + private static void AssertRoundTrip(T value) + { + var json = JsonSerializer.Serialize(value); + var deserialized = JsonSerializer.Deserialize(json); + Assert.Equal(value, deserialized); + } +} diff --git a/Speckle.DoubleNumerics/ConversionExtensions.cs b/Speckle.DoubleNumerics/ConversionExtensions.cs new file mode 100644 index 0000000..f137160 --- /dev/null +++ b/Speckle.DoubleNumerics/ConversionExtensions.cs @@ -0,0 +1,168 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if NET6_0_OR_GREATER +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace Speckle.DoubleNumerics; + +/// +/// Provides extension methods for converting between different numeric types. +/// +public static class ConversionExtensions +{ + // TODO: When targeting .NET 8 and above, we can use the new Unsafe.BitCast method to reinterpret types. + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Vector4 AsVector4(this Plane value) => Unsafe.As(ref value); + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Vector256 AsVector256(this Plane value) => Unsafe.As>(ref value); + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Vector4 AsVector4(this Quaternion value) => Unsafe.As(ref value); + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Vector256 AsVector256(this Quaternion value) => + Unsafe.As>(ref value); + + /// + /// Reinterprets a as a new with the new element zeroed. + /// + /// The to convert. + /// reinterpreted as a new with the new element zeroed. + public static Vector3 AsVector3(this Vector2 value) => new(value, 0); + + /// + /// Reinterprets a as a new with the new elements zeroed. + /// + /// The to convert. + /// reinterpreted as a new with the new elements zeroed. + public static Vector4 AsVector4(this Vector2 value) => new(value, 0, 0); + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Vector256 AsVector256(this Vector2 value) => value.AsVector4().AsVector256(); + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// reinterpreted as a new . + public static Vector2 AsVector2(this Vector3 value) => value.AsVector256().AsVector2(); + + /// + /// Reinterprets a as a new with the new element zeroed. + /// + /// The to convert. + /// reinterpreted as a new with the new element zeroed. + public static Vector4 AsVector4(this Vector3 value) => new(value, 0); + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Vector256 AsVector256(this Vector3 value) => value.AsVector4().AsVector256(); + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Quaternion AsQuaternion(this Vector4 value) => Unsafe.As(ref value); + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Plane AsPlane(this Vector4 value) => Unsafe.As(ref value); + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Vector2 AsVector2(this Vector4 value) => value.AsVector256().AsVector2(); + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Vector3 AsVector3(this Vector4 value) => value.AsVector256().AsVector3(); + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Vector256 AsVector256(this Vector4 value) => Unsafe.As>(ref value); + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Plane AsPlane(this Vector256 value) => Unsafe.As, Plane>(ref value); + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Quaternion AsQuaternion(this Vector256 value) => + Unsafe.As, Quaternion>(ref value); + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Vector2 AsVector2(this Vector256 value) + { + ref byte address = ref Unsafe.As, byte>(ref value); + return Unsafe.ReadUnaligned(ref address); + } + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Vector3 AsVector3(this Vector256 value) + { + ref byte address = ref Unsafe.As, byte>(ref value); + return Unsafe.ReadUnaligned(ref address); + } + + /// + /// Reinterprets a as a new . + /// + /// The to convert. + /// A representation of the . + public static Vector4 AsVector4(this Vector256 value) => Unsafe.As, Vector4>(ref value); +} +#endif diff --git a/Speckle.DoubleNumerics/Plane.cs b/Speckle.DoubleNumerics/Plane.cs index b3233fb..dff14b2 100644 --- a/Speckle.DoubleNumerics/Plane.cs +++ b/Speckle.DoubleNumerics/Plane.cs @@ -10,7 +10,7 @@ namespace Speckle.DoubleNumerics; /// /// A structure encapsulating a 3D Plane /// -public struct Plane : IEquatable +public partial struct Plane : IEquatable { /// /// The normal vector of the Plane. diff --git a/Speckle.DoubleNumerics/Plane_Converter.cs b/Speckle.DoubleNumerics/Plane_Converter.cs new file mode 100644 index 0000000..de6d42c --- /dev/null +++ b/Speckle.DoubleNumerics/Plane_Converter.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if NET6_0_OR_GREATER +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Speckle.DoubleNumerics; + +[JsonConverter(typeof(PlaneJsonConverter))] +public partial struct Plane; + +/// +/// Converter for to and from JSON. +/// +public class PlaneJsonConverter : JsonConverter +{ + public override Plane Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + + Vector3 normal = default; + double d = 0; + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + return new Plane(normal, d); + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException(); + } + + var propertyName = reader.GetString(); + + reader.Read(); + + var comparison = options.PropertyNameCaseInsensitive + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + if (string.Equals(propertyName, nameof(Plane.Normal), comparison)) + { + var vectorConverter = (JsonConverter)options.GetConverter(typeof(Vector3)); + normal = vectorConverter.Read(ref reader, typeof(Vector3), options); + } + else if (string.Equals(propertyName, nameof(Plane.D), comparison)) + { + d = reader.GetDouble(); + } + else + { + reader.Skip(); + } + } + + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, Plane value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName(ConvertName(nameof(value.Normal), options)); + var vectorConverter = (JsonConverter)options.GetConverter(typeof(Vector3)); + vectorConverter.Write(writer, value.Normal, options); + writer.WriteNumber(ConvertName(nameof(value.D), options), value.D); + writer.WriteEndObject(); + } + + private static string ConvertName(string name, JsonSerializerOptions options) => + options.PropertyNamingPolicy?.ConvertName(name) ?? name; +} +#endif diff --git a/Speckle.DoubleNumerics/Quaternion.cs b/Speckle.DoubleNumerics/Quaternion.cs index a611bce..38bbff7 100644 --- a/Speckle.DoubleNumerics/Quaternion.cs +++ b/Speckle.DoubleNumerics/Quaternion.cs @@ -10,7 +10,7 @@ namespace Speckle.DoubleNumerics; /// A structure encapsulating a four-dimensional vector (x,y,z,w), /// which is used to efficiently rotate an object about the (x,y,z) vector by the angle theta, where w = cos(theta/2). /// -public struct Quaternion : IEquatable +public partial struct Quaternion : IEquatable { /// /// Specifies the X-value of the vector component of the Quaternion. diff --git a/Speckle.DoubleNumerics/Quaternion_Converter.cs b/Speckle.DoubleNumerics/Quaternion_Converter.cs new file mode 100644 index 0000000..5bd2215 --- /dev/null +++ b/Speckle.DoubleNumerics/Quaternion_Converter.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if NET6_0_OR_GREATER +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Speckle.DoubleNumerics; + +[JsonConverter(typeof(QuaternionJsonConverter))] +public partial struct Quaternion; + +/// +/// Converter for to and from JSON. +/// +public class QuaternionJsonConverter : JsonConverter +{ + public override Quaternion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + + double x = 0; + double y = 0; + double z = 0; + double w = 0; + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + return new Quaternion(x, y, z, w); + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException(); + } + + var propertyName = reader.GetString(); + + reader.Read(); + + var comparison = options.PropertyNameCaseInsensitive + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + if (string.Equals(propertyName, nameof(Quaternion.X), comparison)) + { + x = reader.GetDouble(); + } + else if (string.Equals(propertyName, nameof(Quaternion.Y), comparison)) + { + y = reader.GetDouble(); + } + else if (string.Equals(propertyName, nameof(Quaternion.Z), comparison)) + { + z = reader.GetDouble(); + } + else if (string.Equals(propertyName, nameof(Quaternion.W), comparison)) + { + w = reader.GetDouble(); + } + else + { + reader.Skip(); + } + } + + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, Quaternion value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteNumber(ConvertName(nameof(value.X), options), value.X); + writer.WriteNumber(ConvertName(nameof(value.Y), options), value.Y); + writer.WriteNumber(ConvertName(nameof(value.Z), options), value.Z); + writer.WriteNumber(ConvertName(nameof(value.W), options), value.W); + writer.WriteEndObject(); + } + + private static string ConvertName(string name, JsonSerializerOptions options) => + options.PropertyNamingPolicy?.ConvertName(name) ?? name; +} +#endif diff --git a/Speckle.DoubleNumerics/Vector2_Converter.cs b/Speckle.DoubleNumerics/Vector2_Converter.cs new file mode 100644 index 0000000..862537a --- /dev/null +++ b/Speckle.DoubleNumerics/Vector2_Converter.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if NET6_0_OR_GREATER +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Speckle.DoubleNumerics; + +[JsonConverter(typeof(Vector2JsonConverter))] +public partial struct Vector2; + +/// +/// Converter for to and from JSON. +/// +public class Vector2JsonConverter : JsonConverter +{ + public override Vector2 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + + double x = 0; + double y = 0; + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + return new Vector2(x, y); + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException(); + } + + var propertyName = reader.GetString(); + + reader.Read(); + + var comparison = options.PropertyNameCaseInsensitive + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + if (string.Equals(propertyName, nameof(Vector2.X), comparison)) + { + x = reader.GetDouble(); + } + else if (string.Equals(propertyName, nameof(Vector2.Y), comparison)) + { + y = reader.GetDouble(); + } + else + { + reader.Skip(); + } + } + + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, Vector2 value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteNumber(ConvertName(nameof(value.X), options), value.X); + writer.WriteNumber(ConvertName(nameof(value.Y), options), value.Y); + writer.WriteEndObject(); + } + + private static string ConvertName(string name, JsonSerializerOptions options) => + options.PropertyNamingPolicy?.ConvertName(name) ?? name; +} +#endif diff --git a/Speckle.DoubleNumerics/Vector3_Converter.cs b/Speckle.DoubleNumerics/Vector3_Converter.cs new file mode 100644 index 0000000..77bf842 --- /dev/null +++ b/Speckle.DoubleNumerics/Vector3_Converter.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if NET6_0_OR_GREATER +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Speckle.DoubleNumerics; + +[JsonConverter(typeof(Vector3JsonConverter))] +public partial struct Vector3; + +/// +/// Converter for to and from JSON. +/// +public class Vector3JsonConverter : JsonConverter +{ + public override Vector3 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + + double x = 0; + double y = 0; + double z = 0; + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + return new Vector3(x, y, z); + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException(); + } + + var propertyName = reader.GetString(); + + reader.Read(); + + var comparison = options.PropertyNameCaseInsensitive + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + if (string.Equals(propertyName, nameof(Vector3.X), comparison)) + { + x = reader.GetDouble(); + } + else if (string.Equals(propertyName, nameof(Vector3.Y), comparison)) + { + y = reader.GetDouble(); + } + else if (string.Equals(propertyName, nameof(Vector3.Z), comparison)) + { + z = reader.GetDouble(); + } + else + { + reader.Skip(); + } + } + + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, Vector3 value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteNumber(ConvertName(nameof(value.X), options), value.X); + writer.WriteNumber(ConvertName(nameof(value.Y), options), value.Y); + writer.WriteNumber(ConvertName(nameof(value.Z), options), value.Z); + writer.WriteEndObject(); + } + + private static string ConvertName(string name, JsonSerializerOptions options) => + options.PropertyNamingPolicy?.ConvertName(name) ?? name; +} +#endif diff --git a/Speckle.DoubleNumerics/Vector4_Converter.cs b/Speckle.DoubleNumerics/Vector4_Converter.cs new file mode 100644 index 0000000..7bf1f13 --- /dev/null +++ b/Speckle.DoubleNumerics/Vector4_Converter.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if NET6_0_OR_GREATER +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Speckle.DoubleNumerics; + +[JsonConverter(typeof(Vector4JsonConverter))] +public partial struct Vector4; + +/// +/// Converter for to and from JSON. +/// +public class Vector4JsonConverter : JsonConverter +{ + public override Vector4 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + + double x = 0; + double y = 0; + double z = 0; + double w = 0; + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + return new Vector4(x, y, z, w); + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException(); + } + + var propertyName = reader.GetString(); + + reader.Read(); + + var comparison = options.PropertyNameCaseInsensitive + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + if (string.Equals(propertyName, nameof(Vector4.X), comparison)) + { + x = reader.GetDouble(); + } + else if (string.Equals(propertyName, nameof(Vector4.Y), comparison)) + { + y = reader.GetDouble(); + } + else if (string.Equals(propertyName, nameof(Vector4.Z), comparison)) + { + z = reader.GetDouble(); + } + else if (string.Equals(propertyName, nameof(Vector4.W), comparison)) + { + w = reader.GetDouble(); + } + else + { + reader.Skip(); + } + } + + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, Vector4 value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteNumber(ConvertName(nameof(value.X), options), value.X); + writer.WriteNumber(ConvertName(nameof(value.Y), options), value.Y); + writer.WriteNumber(ConvertName(nameof(value.Z), options), value.Z); + writer.WriteNumber(ConvertName(nameof(value.W), options), value.W); + writer.WriteEndObject(); + } + + private static string ConvertName(string name, JsonSerializerOptions options) => + options.PropertyNamingPolicy?.ConvertName(name) ?? name; +} +#endif