From a554e986db22e404927c42c0164ef8b50801f8af Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Mon, 1 Jun 2020 00:19:28 +0200 Subject: [PATCH] Continued improvements in coverage Fixed using declarations in may files. --- .editorconfig | 67 +- .travis.yml | 1 + .vscode/extensions.json | 2 +- Paramdigma.Core.sln.DotSettings | 4 + coverage/cobertura.xml | 932 +++++++++--------- src/Collections/Interval.cs | 8 +- src/Curves/Geodesics.cs | 2 - src/Curves/LevelSets.cs | 1 - src/Exceptions/UnsetGeometryException.cs | 19 + src/Geometry/2D/Ray2d.cs | 15 +- src/Geometry/2D/Vector2d.cs | 24 +- src/Geometry/3D/Line.cs | 15 +- src/Geometry/3D/Mesh/Mesh.cs | 4 +- src/Geometry/3D/Mesh/MeshEdge.cs | 3 +- src/Geometry/3D/Mesh/MeshGeometry.cs | 1 - src/Geometry/3D/Mesh/MeshHalfEdge.cs | 4 +- src/Geometry/3D/Mesh/MeshPoint.cs | 1 - src/Geometry/3D/Mesh/MeshTopology.cs | 3 +- src/Geometry/3D/Mesh/MeshVertex.cs | 3 +- src/Geometry/3D/Plane.cs | 6 +- src/Geometry/3D/Point3d.cs | 7 +- src/Geometry/3D/Point4d.cs | 31 +- src/Geometry/3D/Polyline.cs | 128 ++- src/Geometry/3D/Primitives/Cylinder.cs | 4 + src/Geometry/3D/Primitives/Sphere.cs | 2 + src/Geometry/3D/Primitives/Torus.cs | 6 + src/Geometry/3D/Ray.cs | 6 +- src/Geometry/3D/Vector3d.cs | 3 +- src/Geometry/Base/BaseCurve.cs | 3 +- src/Geometry/Base/BasePoint.cs | 12 +- src/Geometry/Interfaces/ISurface.cs | 28 +- src/Geometry/Intersect/Intersect.cs | 9 +- src/Geometry/SpatialStructures/PointCloud.cs | 3 - .../SpatialStructures/PointCloudMember.cs | 2 - src/IO/OBJWritter.cs | 5 +- src/LinearAlgebra/Triplet.cs | 3 +- src/Utility/Convert.cs | 6 - src/Utility/Settings.cs | 9 +- tests/Collections/IntervalTests.cs | 8 +- tests/Extensions/ListExtensionsTests.cs | 29 + tests/Geometry/2D/Polyline2dTests.cs | 1 - tests/Geometry/2D/Ray2dTests.cs | 20 + tests/Geometry/3D/Intersect3dTests.cs | 76 ++ tests/Geometry/3D/LineTests.cs | 2 - tests/Geometry/3D/NurbsTests.cs | 4 - tests/Geometry/3D/PlaneTests.cs | 41 + tests/Geometry/3D/Point3dTests.cs | 37 +- tests/Geometry/3D/Point4dTests.cs | 138 +++ tests/Geometry/3D/Polyline3dTests.cs | 176 ++++ tests/Geometry/3D/Ray3dTests.cs | 29 + tests/Utilities/ResourcesTests.cs | 29 +- 51 files changed, 1318 insertions(+), 654 deletions(-) create mode 100644 Paramdigma.Core.sln.DotSettings create mode 100644 src/Exceptions/UnsetGeometryException.cs create mode 100644 tests/Extensions/ListExtensionsTests.cs create mode 100644 tests/Geometry/2D/Ray2dTests.cs create mode 100644 tests/Geometry/3D/Intersect3dTests.cs create mode 100644 tests/Geometry/3D/Point4dTests.cs create mode 100644 tests/Geometry/3D/Polyline3dTests.cs create mode 100644 tests/Geometry/3D/Ray3dTests.cs diff --git a/.editorconfig b/.editorconfig index 7c9792f..df5e28a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,44 +2,6 @@ root=true # Microsoft .NET properties csharp_new_line_before_members_in_object_initializers=true -# ReSharper properties -resharper_align_multiline_argument=true -resharper_align_multiline_expression=true -resharper_align_multiple_declaration=true -resharper_align_multline_type_parameter_constrains=true -resharper_align_multline_type_parameter_list=true -resharper_align_tuple_components=true -resharper_blank_lines_after_control_transfer_statements=1 -resharper_blank_lines_before_block_statements=1 -resharper_braces_for_dowhile=required_for_multiline -resharper_braces_for_fixed=required_for_multiline -resharper_braces_for_for=required_for_multiline -resharper_braces_for_foreach=required_for_multiline -resharper_braces_for_ifelse=required_for_multiline -resharper_braces_for_lock=required_for_multiline -resharper_braces_for_using=required_for_multiline -resharper_braces_for_while=required_for_multiline -resharper_constructor_or_destructor_body=expression_body -resharper_csharp_wrap_before_binary_opsign=true -resharper_csharp_wrap_extends_list_style=chop_if_long -resharper_csharp_wrap_parameters_style=chop_if_long -resharper_empty_block_style=together_same_line -resharper_indent_nested_foreach_stmt=true -resharper_indent_nested_for_stmt=true -resharper_keep_existing_invocation_parens_arrangement=false -resharper_keep_existing_switch_expression_arrangement=false -resharper_local_function_body=expression_body -resharper_max_array_initializer_elements_on_line=8 -resharper_max_formal_parameters_on_line=3 -resharper_max_initializer_elements_on_line=3 -resharper_method_or_operator_body=expression_body -resharper_outdent_commas=true -resharper_place_simple_switch_expression_on_single_line=true -resharper_wrap_array_initializer_style=chop_if_long -resharper_wrap_chained_binary_expressions=chop_if_long -resharper_wrap_enum_declaration=chop_if_long -resharper_wrap_switch_expression=chop_if_long - ########################################## # File Extension Settings ########################################## @@ -106,10 +68,10 @@ visual_basic_preferred_modifier_order=Partial,Default,Private,Protected,Public,F dotnet_style_readonly_field=true:warning # Parentheses preferences # https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parentheses-preferences -# dotnet_style_parentheses_in_arithmetic_binary_operators=always_for_clarity:suggestion -# dotnet_style_parentheses_in_relational_binary_operators=always_for_clarity:suggestion -# dotnet_style_parentheses_in_other_binary_operators=always_for_clarity:suggestion -# dotnet_style_parentheses_in_other_operators=always_for_clarity:suggestion +dotnet_style_parentheses_in_arithmetic_binary_operators=always_for_clarity:suggestion +dotnet_style_parentheses_in_relational_binary_operators=always_for_clarity:suggestion +dotnet_style_parentheses_in_other_binary_operators=always_for_clarity:suggestion +dotnet_style_parentheses_in_other_operators=always_for_clarity:suggestion # Expression-level preferences # https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences dotnet_style_object_initializer=true:warning @@ -403,6 +365,27 @@ dotnet_naming_rule.parameters_rule.symbols=parameters_group dotnet_naming_rule.parameters_rule.style=camel_case_style dotnet_naming_rule.parameters_rule.severity=warning +# Microsoft .NET properties +csharp_prefer_braces=false:none +csharp_space_after_keywords_in_control_flow_statements=true +csharp_space_between_method_call_parameter_list_parentheses=false +csharp_space_between_method_declaration_parameter_list_parentheses=false +csharp_space_between_parentheses=false + +# ReSharper properties +resharper_braces_redundant=true +resharper_empty_block_style=together_same_line +resharper_local_function_body=expression_body +resharper_space_within_catch_parentheses=false +resharper_space_within_checked_parentheses=false +resharper_space_within_foreach_parentheses=false +resharper_space_within_for_parentheses=false +resharper_space_within_if_parentheses=false +resharper_space_within_parentheses=false +resharper_space_within_switch_parentheses=false +resharper_space_within_using_parentheses=false +resharper_space_within_while_parentheses=false + ########################################## # License ########################################## diff --git a/.travis.yml b/.travis.yml index f4b043c..d3b7017 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ branches: only: - master - development + - feature/* notifications: slack: paramdigma:FtUcdwSDOynOHw2M479kXHxs#geometry-library diff --git a/.vscode/extensions.json b/.vscode/extensions.json index a67b316..a9c5f85 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -5,7 +5,7 @@ // List of extensions which should be recommended for users of this workspace. "recommendations": [ "ban.spellright", - "ms-vscode.csharp", + "ms-dotnettools.csharp", "Gruntfuggly.todo-tree", "brainfit.vscode-coverage-highlighter", "josefpihrt-vscode.snippetica-csharp", diff --git a/Paramdigma.Core.sln.DotSettings b/Paramdigma.Core.sln.DotSettings new file mode 100644 index 0000000..0b65484 --- /dev/null +++ b/Paramdigma.Core.sln.DotSettings @@ -0,0 +1,4 @@ + + True + True + True \ No newline at end of file diff --git a/coverage/cobertura.xml b/coverage/cobertura.xml index 34a95ee..b2695c4 100644 --- a/coverage/cobertura.xml +++ b/coverage/cobertura.xml @@ -1,10 +1,10 @@  - + - + @@ -590,7 +590,7 @@ - + @@ -640,7 +640,7 @@ - + @@ -7644,31 +7644,31 @@ - + - + - + - + - + - + @@ -7678,74 +7678,74 @@ - + - + - - - - - - + + + + + + - + - + - + - - - - - - - + + + + + + + - + - + - - + - + + - + - - + + - + - + @@ -7764,232 +7764,239 @@ - - - + + + - - - - - - - + + + + + + + - + - - - - - - - - - + + + + + + + + + + - + - - + + - - + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + - - - - + + + + - + - + - + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + + + - - - + + + @@ -8001,9 +8008,9 @@ - - - + + + @@ -8015,151 +8022,152 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - + - + - - - + + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - - + + - + - - - + + + - - - + + + - + - - - - + + + + - - - - + + + + @@ -8170,58 +8178,70 @@ + + + + + + + + - - - - + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - + - - + + - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + @@ -8662,22 +8682,22 @@ - + - + - - - - - - + + + + + + @@ -8693,27 +8713,27 @@ - + - + - + - - - - - - + + + + + + @@ -8725,17 +8745,17 @@ - + - + - + @@ -8755,12 +8775,12 @@ - + - + @@ -8780,7 +8800,7 @@ - + @@ -8802,9 +8822,9 @@ - - - + + + @@ -8816,21 +8836,21 @@ - - - + + + - - - - - - - - + + + + + + + + @@ -8838,44 +8858,44 @@ - - - - - - - - - + + + + + + + + + - - - + + + - - + + - + - - - + + + - - - + + + @@ -8914,306 +8934,306 @@ - + - - - + + + - + - - + + - + - - - + + + - - - + + + - - + + - - + + - - - + + + - - + + - - + + - + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - + - + - - + + - - - - + + + + - - - + + + - - - - - - - - + + + + + + + + - - - - + + + + - - - + + + - - - - - - - + + + + + + + - - - - - + + + + + - + - - - - - - - - + + + + + + + + - - - - - - - + + + + + + + - - - - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + + + + - - - - + + + + - - - - - - - - + + + - - - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Collections/Interval.cs b/src/Collections/Interval.cs index fa36d8d..67821e2 100644 --- a/src/Collections/Interval.cs +++ b/src/Collections/Interval.cs @@ -14,12 +14,10 @@ public struct Interval /// Ending value of the interval. public Interval(double start, double end) { - if (start == end) - throw new Exception("Cannot create Interval out of two equal numbers"); if (double.IsNaN(start) || double.IsInfinity(start)) - throw new Exception("Start value is invalid"); + throw new ArithmeticException("Start value is invalid"); if (double.IsNaN(end) || double.IsInfinity(end)) - throw new Exception("End value is invalid"); + throw new ArithmeticException("End value is invalid"); this.Start = start; this.End = end; } @@ -130,7 +128,7 @@ public bool Contains(double number) { double min = this.HasInvertedDirection ? this.End : this.Start; double max = this.HasInvertedDirection ? this.Start : this.End; - return min < number && number < max; + return min <= number && number <= max; } /// diff --git a/src/Curves/Geodesics.cs b/src/Curves/Geodesics.cs index 4ccbfe2..93329da 100644 --- a/src/Curves/Geodesics.cs +++ b/src/Curves/Geodesics.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Generic; -using Paramdigma.Core; using Paramdigma.Core.Geometry; using Paramdigma.Core.HalfEdgeMesh; diff --git a/src/Curves/LevelSets.cs b/src/Curves/LevelSets.cs index cd7f154..95a5ae3 100644 --- a/src/Curves/LevelSets.cs +++ b/src/Curves/LevelSets.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using Paramdigma.Core.Geometry; using Paramdigma.Core.HalfEdgeMesh; diff --git a/src/Exceptions/UnsetGeometryException.cs b/src/Exceptions/UnsetGeometryException.cs new file mode 100644 index 0000000..ce252d6 --- /dev/null +++ b/src/Exceptions/UnsetGeometryException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Paramdigma.Core.Exceptions +{ + /// + /// Represents errors that ocur when using a geometry that has the 'isUnset' flag set to true. + /// + public class UnsetGeometryException : Exception + { + /// + public UnsetGeometryException() { } + + /// + public UnsetGeometryException( string message ) : base( message ) { } + + /// + public UnsetGeometryException( string message, Exception innerException ) { } + } +} \ No newline at end of file diff --git a/src/Geometry/2D/Ray2d.cs b/src/Geometry/2D/Ray2d.cs index ef6a342..0a5d0db 100644 --- a/src/Geometry/2D/Ray2d.cs +++ b/src/Geometry/2D/Ray2d.cs @@ -1,10 +1,23 @@ +using System; + namespace Paramdigma.Core.Geometry { /// - /// Represents a 2-dimensional ray. + /// Represents an infinite 2-dimensional ray. /// public class Ray2d { + /// + /// Initializes a new instance of the class. + /// + /// Origin point. + /// Direction vector. + public Ray2d(Point2d origin, Vector2d direction) + { + this.Origin = origin ?? throw new ArgumentNullException(nameof(origin)); + this.Direction = direction ?? throw new ArgumentNullException(nameof(direction)); + } + /// /// Gets or sets the origin of the ray. /// diff --git a/src/Geometry/2D/Vector2d.cs b/src/Geometry/2D/Vector2d.cs index ef054ae..e80f0e2 100644 --- a/src/Geometry/2D/Vector2d.cs +++ b/src/Geometry/2D/Vector2d.cs @@ -13,7 +13,8 @@ public class Vector2d /// X coordinate. public double X { - get; set; + get; + set; } /// @@ -22,7 +23,8 @@ public double X /// Y coordinate. public double Y { - get; set; + get; + set; } /// @@ -31,9 +33,7 @@ public double Y /// Vector to duplicate. /// New vector with same values. public Vector2d(Vector2d vector) - : this(vector.X, vector.Y) - { - } + : this(vector.X, vector.Y) { } /// /// Initializes a new instance of the class from a point. @@ -41,9 +41,7 @@ public Vector2d(Vector2d vector) /// Point to convert. /// New vector with same values. public Vector2d(Point2d point) - : this(point.X, point.Y) - { - } + : this(point.X, point.Y) { } /// /// Initializes a new instance of the class. @@ -66,6 +64,16 @@ public Vector2d(double x, double y) /// public double Length => Math.Sqrt(this.LengthSquared); + /// + /// Creates a vector in the World X direction {1;0} + /// + public static Vector2d WorldX => new Vector2d(1, 0); + + /// + /// Creates a vector in the World Y direction {0;1} + /// + public static Vector2d WorldY => new Vector2d(0, 1); + /// /// Returns a unit vector of this vector. /// diff --git a/src/Geometry/3D/Line.cs b/src/Geometry/3D/Line.cs index 78488b9..85966b8 100644 --- a/src/Geometry/3D/Line.cs +++ b/src/Geometry/3D/Line.cs @@ -1,3 +1,6 @@ +using System; +using Paramdigma.Core.Collections; + namespace Paramdigma.Core.Geometry { /// @@ -10,15 +13,17 @@ public class Line : BaseCurve /// public Point3d StartPoint { - get; set; + get; + set; } /// - /// /// Gets or sets the line's end point. + /// Gets or sets the line's end point. /// public Point3d EndPoint { - get; set; + get; + set; } /// @@ -55,7 +60,7 @@ public Line(Point3d origin, Vector3d direction, double length) /// /// Parameter of the point. Must be between 0 and 1. /// Point at specified parameter. - public override Point3d PointAt(double t) => this.StartPoint + (t * (this.EndPoint - this.StartPoint)); + public override Point3d PointAt(double t) => this.StartPoint + (Domain.RemapToUnit(t) * (this.EndPoint - this.StartPoint)); /// /// Computes the tangent at the given parameter. @@ -79,7 +84,7 @@ public override Vector3d NormalAt(double t) Vector3d tangent = TangentAt(t); var v = new Vector3d(); - if (tangent.Dot(Vector3d.UnitZ) == 1) + if (Math.Abs(tangent.Dot(Vector3d.UnitZ) - 1) < Settings.Tolerance) v = Vector3d.UnitX; else v = Vector3d.UnitZ; diff --git a/src/Geometry/3D/Mesh/Mesh.cs b/src/Geometry/3D/Mesh/Mesh.cs index dae34e9..05e0853 100644 --- a/src/Geometry/3D/Mesh/Mesh.cs +++ b/src/Geometry/3D/Mesh/Mesh.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using Paramdigma.Core; +using System.Collections.Generic; using Paramdigma.Core.Geometry; namespace Paramdigma.Core.HalfEdgeMesh diff --git a/src/Geometry/3D/Mesh/MeshEdge.cs b/src/Geometry/3D/Mesh/MeshEdge.cs index da4e892..686b9a1 100644 --- a/src/Geometry/3D/Mesh/MeshEdge.cs +++ b/src/Geometry/3D/Mesh/MeshEdge.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace Paramdigma.Core.HalfEdgeMesh { diff --git a/src/Geometry/3D/Mesh/MeshGeometry.cs b/src/Geometry/3D/Mesh/MeshGeometry.cs index 89da595..6f2524e 100644 --- a/src/Geometry/3D/Mesh/MeshGeometry.cs +++ b/src/Geometry/3D/Mesh/MeshGeometry.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Paramdigma.Core.HalfEdgeMesh; namespace Paramdigma.Core.Geometry diff --git a/src/Geometry/3D/Mesh/MeshHalfEdge.cs b/src/Geometry/3D/Mesh/MeshHalfEdge.cs index 28f0094..876f696 100644 --- a/src/Geometry/3D/Mesh/MeshHalfEdge.cs +++ b/src/Geometry/3D/Mesh/MeshHalfEdge.cs @@ -1,6 +1,4 @@ -using System; - -namespace Paramdigma.Core.HalfEdgeMesh +namespace Paramdigma.Core.HalfEdgeMesh { /// /// Represents a mesh half-edge. diff --git a/src/Geometry/3D/Mesh/MeshPoint.cs b/src/Geometry/3D/Mesh/MeshPoint.cs index c172f79..2da9ca1 100644 --- a/src/Geometry/3D/Mesh/MeshPoint.cs +++ b/src/Geometry/3D/Mesh/MeshPoint.cs @@ -1,4 +1,3 @@ -using System.Collections; using System.Collections.Generic; using Paramdigma.Core.Geometry; diff --git a/src/Geometry/3D/Mesh/MeshTopology.cs b/src/Geometry/3D/Mesh/MeshTopology.cs index e583787..39c590c 100644 --- a/src/Geometry/3D/Mesh/MeshTopology.cs +++ b/src/Geometry/3D/Mesh/MeshTopology.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace Paramdigma.Core.HalfEdgeMesh { diff --git a/src/Geometry/3D/Mesh/MeshVertex.cs b/src/Geometry/3D/Mesh/MeshVertex.cs index 2d171db..0e37e68 100644 --- a/src/Geometry/3D/Mesh/MeshVertex.cs +++ b/src/Geometry/3D/Mesh/MeshVertex.cs @@ -1,5 +1,4 @@ -using System.Collections; -using System.Collections.Generic; +using System.Collections.Generic; using Paramdigma.Core.Geometry; namespace Paramdigma.Core.HalfEdgeMesh diff --git a/src/Geometry/3D/Plane.cs b/src/Geometry/3D/Plane.cs index f7f80b6..c864025 100644 --- a/src/Geometry/3D/Plane.cs +++ b/src/Geometry/3D/Plane.cs @@ -127,8 +127,10 @@ public Plane(Point3d ptA, Point3d ptB, Point3d ptC) Vector3d normal = tempX.Cross(tempY); double colinearCheck = Math.Abs(1 - tempY.Dot(tempX)); + var compare = tempX.Dot(tempY); + // Ensure points are not co-linear - if (tempY.Dot(tempX) == 1) + if (Math.Abs(compare - 1) <= Settings.Tolerance) throw new System.Exception("Cannot create plane out of co-linear points."); Origin = ptA; @@ -220,7 +222,7 @@ public Point3d ClosestPoint(Point3d point) /// /// Point to compute distance to. /// Distance to point. - public double DistanceTo(Point3d point) => ((Vector3d)(point - Origin)).Dot(ZAxis); + public double DistanceTo(Point3d point) => (point - Origin).Dot(ZAxis); /// /// Returns the parametric equation for this plane. diff --git a/src/Geometry/3D/Point3d.cs b/src/Geometry/3D/Point3d.cs index a6a40eb..741f743 100644 --- a/src/Geometry/3D/Point3d.cs +++ b/src/Geometry/3D/Point3d.cs @@ -70,6 +70,12 @@ public Point3d(Point4d point) /// public static Point3d WorldOrigin => new Point3d(0, 0, 0); + /// + /// Performs a deep clone of the point. + /// + /// Returns a copy of this point instance. + public Point3d Clone() => new Point3d(this.X, this.Y, this.Z); + /// /// Gets the euclidean distance between this point and the provided one. /// @@ -141,7 +147,6 @@ public Point3d(Point4d point) /// . public static Point3d operator /(Point3d point, double scalar) => new Point3d(point.X / scalar, point.Y / scalar, point.Z / scalar); - /// /// Checks equality between two points. /// diff --git a/src/Geometry/3D/Point4d.cs b/src/Geometry/3D/Point4d.cs index 752f933..6de279c 100644 --- a/src/Geometry/3D/Point4d.cs +++ b/src/Geometry/3D/Point4d.cs @@ -1,8 +1,4 @@ using System; -using System.Collections; -using System.Collections.Generic; -using Paramdigma.Core.Geometry; -using Paramdigma.Core.LinearAlgebra; namespace Paramdigma.Core.Geometry { @@ -49,12 +45,24 @@ public Point4d(Point3d pt, double w) weight = w; } + /// + /// Initializes a new instance of the class from a 3-dimensional point and a weight. + /// + /// Point. + /// New 4-dimensional point with the specified values. + public Point4d(Point3d pt) + : base(pt) + { + weight = 1; + } + /// /// Gets or sets the weight of this point. /// public double Weight { - get => weight; set + get => weight; + set { weight = value; if (IsUnset) @@ -62,6 +70,11 @@ public double Weight } } + /// + /// Returns the raw position of the point4d (without taking into account weight). + /// + public Point3d Position => new Point3d(X, Y, Z); + /// public static Point4d operator +(Point4d point, Point4d point2) => new Point4d(point.X + point2.X, point.Y + point2.Y, point.Z + point2.Z, point.Weight + point2.Weight); @@ -80,9 +93,6 @@ public double Weight /// public static Point4d operator /(Point4d point, double scalar) => new Point4d(point.X / scalar, point.Y / scalar, point.Z / scalar, point.Weight / scalar); - /// - public static Point4d operator /(double scalar, Point4d point) => new Point4d(point.X / scalar, point.Y / scalar, point.Z / scalar, point.Weight / scalar); - /// public static bool operator ==(Point4d point, Point4d point2) => point.Equals(point2); @@ -97,7 +107,8 @@ public override bool Equals(object obj) { if (obj is Point4d pt) { - return base.Equals(obj) && this.Weight == pt.Weight; + return base.Equals(obj) + && Math.Abs(this.Weight - pt.Weight) < Settings.Tolerance; } else { @@ -114,4 +125,4 @@ public override int GetHashCode() // TODO: Add hasWeightedCoordinates boolean and implement a weightCoordinates() method } -} +} \ No newline at end of file diff --git a/src/Geometry/3D/Polyline.cs b/src/Geometry/3D/Polyline.cs index 078026a..ef1780f 100644 --- a/src/Geometry/3D/Polyline.cs +++ b/src/Geometry/3D/Polyline.cs @@ -1,6 +1,8 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; +using Paramdigma.Core.Exceptions; namespace Paramdigma.Core.Geometry { @@ -9,10 +11,32 @@ namespace Paramdigma.Core.Geometry /// public class Polyline : BaseCurve, IEnumerable { + // TODO: Frame, Normal, Binormal and Tangent calculation is still slightly sketchy. It must be checked. private readonly List knots; private List segments; private bool segmentsNeedUpdate; + /// + /// Initializes a new instance of the class. + /// + public Polyline() + { + knots = new List(); + segments = new List(); + segmentsNeedUpdate = false; + } + + /// + /// Initializes a new instance of the class from a list of points. + /// + /// List of points. + public Polyline(List knots) + { + this.knots = knots; + segments = new List(); + this.RebuildSegments(); + } + /// /// Gets the segment lines of the polyline. /// @@ -22,41 +46,37 @@ public List Segments get { if (segmentsNeedUpdate) - RebuildSegments(); + this.RebuildSegments(); return segments; } } /// - /// Gets a value indicating whether the polyline is closed (first point == last point). + /// Gets the knot at the specified index. /// - public bool IsClosed => knots[0] == knots[^1]; + /// The index. + public Point3d this[int index] => this.knots[index]; /// - /// Gets a value indicating whether the polyline is unset. + /// Gets the list of knots for this polyline. /// - public bool IsUnset => knots.Count == 0; + public List Knots => this.knots; /// - /// Initializes a new instance of the class. + /// Gets a value indicating whether the polyline is closed (first point == last point). /// - public Polyline() - { - knots = new List(); - segments = new List(); - segmentsNeedUpdate = false; - } + public bool IsClosed => knots[0] == knots[^1]; /// - /// Initializes a new instance of the class from a list of points. + /// Gets a value indicating whether the polyline is unset. /// - /// List of points. - public Polyline(List knots) - { - this.knots = knots; - segments = new List(); - segmentsNeedUpdate = true; - } + public bool IsUnset => knots.Count == 0; + + /// + public IEnumerator GetEnumerator() => ((IEnumerable)knots).GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)knots).GetEnumerator(); /// /// Add a new knot vertex at the end of the polyline. @@ -73,7 +93,7 @@ public void AddKnot(Point3d knot) /// /// Point to add. /// Location to add at. - public void AddKnot(Point3d knot, int index) + public void InsertKnot(Point3d knot, int index) { knots.Insert(index, knot); // Add knot to list segmentsNeedUpdate = true; @@ -81,27 +101,29 @@ public void AddKnot(Point3d knot, int index) /// /// Delete a specific knot if it exists in the polyline. + /// If the point exists multiple times, it will remove the first occurrence. /// /// Point to delete. public void RemoveKnot(Point3d knot) { - if (knots.Contains(knot)) - { - knots.Remove(knot); - segmentsNeedUpdate = true; - } + if (!this.knots.Contains(knot)) + throw new Exception("Point is not a knot in the polyline"); + this.knots.Remove(knot); + this.segmentsNeedUpdate = true; } /// /// Delete a knot at a specific index. /// /// Index to delete knot at. - public void RemoveKnot(int index) + public void RemoveKnotAt(int index) { if (IsUnset) - throw new Exception("Cannot erase knot from an Unset polyline"); - if (index < 0 || index > segments.Count - 1) + throw new UnsetGeometryException("Cannot erase knot from an Unset polyline"); + if (index < 0 || index > knots.Count - 1) throw new IndexOutOfRangeException("Knot index must be within the Knot list count"); + knots.RemoveAt(index); + this.segmentsNeedUpdate = true; } private void RebuildSegments() @@ -115,26 +137,35 @@ private void RebuildSegments() t += l.Length; var t1 = t; l.Domain = new Collections.Interval(t0, t1); - - // Add segment to list. segments.Add(l); } } /// - public override Vector3d BinormalAt(double t) => throw new NotImplementedException(); + public override Vector3d BinormalAt(double t) => (from segment in this.segments + where segment.Domain.Contains(t) + select segment.BinormalAt(t)).FirstOrDefault(); /// - public override Vector3d NormalAt(double t) => throw new NotImplementedException(); + public override Vector3d NormalAt(double t) => (from segment in this.segments + where segment.Domain.Contains(t) + select segment.NormalAt(t)).FirstOrDefault(); /// - public override Point3d PointAt(double t) => throw new NotImplementedException(); + public override Point3d PointAt(double t) => (from segment in this.segments + where segment.Domain.Contains(t) + select segment.PointAt(t)).FirstOrDefault(); + /// - public override Vector3d TangentAt(double t) => throw new NotImplementedException(); + public override Vector3d TangentAt(double t) => (from segment in this.segments + where segment.Domain.Contains(t) + select segment.TangentAt(t)).FirstOrDefault(); /// - public override Plane FrameAt(double t) => throw new NotImplementedException(); + public override Plane FrameAt(double t) => (from segment in this.segments + where segment.Domain.Contains(t) + select segment.FrameAt(t)).FirstOrDefault(); /// protected override double ComputeLength() @@ -144,13 +175,24 @@ protected override double ComputeLength() return length; } - /// - public override bool CheckValidity() => throw new NotImplementedException(); - - /// - public IEnumerator GetEnumerator() => ((IEnumerable)knots).GetEnumerator(); + /// + /// Checks the validity of the polyline. Currently only checks if some segments are collapsed (length == 0); + /// + /// True if polyline has no collapsed segments. + public override bool CheckValidity() + { + if (IsUnset) + return false; + bool valid = true; + foreach (var segment in this.segments) + { + if (segment.Length > Settings.Tolerance) + continue; + valid = false; + break; + } - /// - IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)knots).GetEnumerator(); + return valid; + } } } \ No newline at end of file diff --git a/src/Geometry/3D/Primitives/Cylinder.cs b/src/Geometry/3D/Primitives/Cylinder.cs index ba14b53..cdf3f89 100644 --- a/src/Geometry/3D/Primitives/Cylinder.cs +++ b/src/Geometry/3D/Primitives/Cylinder.cs @@ -56,6 +56,10 @@ public Plane FrameAt(double u, double v) throw new System.NotImplementedException(); } + public double DistanceTo(Point3d point) => throw new System.NotImplementedException(); + + public Point3d ClosestPointTo(Point3d point) => throw new System.NotImplementedException(); + /// public Vector3d NormalAt(double u, double v) { diff --git a/src/Geometry/3D/Primitives/Sphere.cs b/src/Geometry/3D/Primitives/Sphere.cs index 553b847..17d9704 100644 --- a/src/Geometry/3D/Primitives/Sphere.cs +++ b/src/Geometry/3D/Primitives/Sphere.cs @@ -29,6 +29,8 @@ public class Sphere : ISurface /// public double DistanceTo(Point3d point) => Plane.DistanceTo(point) - Radius; + public Point3d ClosestPointTo(Point3d point) => Plane.Origin + ((point - Plane.Origin).Unit() * Radius); + /// public Plane FrameAt(double u, double v) => throw new System.NotImplementedException(); diff --git a/src/Geometry/3D/Primitives/Torus.cs b/src/Geometry/3D/Primitives/Torus.cs index 6967485..75d9c7a 100644 --- a/src/Geometry/3D/Primitives/Torus.cs +++ b/src/Geometry/3D/Primitives/Torus.cs @@ -48,6 +48,12 @@ public Torus(Plane plane, double majorRadius, double minorRadius) /// public Plane FrameAt(double u, double v) => throw new System.NotImplementedException(); + /// + public double DistanceTo(Point3d point) => throw new System.NotImplementedException(); + + /// + public Point3d ClosestPointTo(Point3d point) => throw new System.NotImplementedException(); + /// public Vector3d NormalAt(double u, double v) => throw new System.NotImplementedException(); diff --git a/src/Geometry/3D/Ray.cs b/src/Geometry/3D/Ray.cs index baba886..e36c870 100644 --- a/src/Geometry/3D/Ray.cs +++ b/src/Geometry/3D/Ray.cs @@ -1,3 +1,5 @@ +using System; + namespace Paramdigma.Core.Geometry { /// @@ -22,8 +24,8 @@ public class Ray /// Vector representing the direction of the ray. public Ray(Point3d origin, Vector3d direction) { - Origin = origin; - Direction = direction; + Origin = origin ?? throw new ArgumentNullException(nameof(origin)); + Direction = direction ?? throw new ArgumentNullException(nameof(direction)); } /// diff --git a/src/Geometry/3D/Vector3d.cs b/src/Geometry/3D/Vector3d.cs index 7894f76..adf6410 100644 --- a/src/Geometry/3D/Vector3d.cs +++ b/src/Geometry/3D/Vector3d.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; namespace Paramdigma.Core.Geometry { @@ -48,7 +47,7 @@ public Vector3d(double xCoord, double yCoord, double zCoord) } /// - /// Gets the Euclidiean length squared of this vector. + /// Gets the Euclidean length squared of this vector. /// /// Squared Length of the vector. public double LengthSquared => DotProduct(this, this); diff --git a/src/Geometry/Base/BaseCurve.cs b/src/Geometry/Base/BaseCurve.cs index f9be678..2ab278a 100644 --- a/src/Geometry/Base/BaseCurve.cs +++ b/src/Geometry/Base/BaseCurve.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using Paramdigma.Core.Collections; +using Paramdigma.Core.Collections; #pragma warning disable 1591 diff --git a/src/Geometry/Base/BasePoint.cs b/src/Geometry/Base/BasePoint.cs index 58579d5..c916147 100644 --- a/src/Geometry/Base/BasePoint.cs +++ b/src/Geometry/Base/BasePoint.cs @@ -67,7 +67,10 @@ public double Z /// Gets or sets a value indicating whether the current point is unset. /// /// True if Unset. - public bool IsUnset { get; set; } + public bool IsUnset + { + get; set; + } private double x; private double y; @@ -173,7 +176,12 @@ public void Negate() /// Converts a point to an array of numbers. /// /// Array with cartesian coordinates of point. - public double[] ToArray() => new double[] { x, y, z }; + public double[] ToArray() => new double[] { this.x, this.y, this.z }; + + /// + /// Performs a deep clone of the point. + /// + /// Returns a copy of this BasePoint instance. /// public override bool Equals(object obj) diff --git a/src/Geometry/Interfaces/ISurface.cs b/src/Geometry/Interfaces/ISurface.cs index f77d097..48f836b 100644 --- a/src/Geometry/Interfaces/ISurface.cs +++ b/src/Geometry/Interfaces/ISurface.cs @@ -11,13 +11,19 @@ public interface ISurface /// Gets the domain in the U direction. /// /// . - Interval DomainU { get; } + Interval DomainU + { + get; + } /// /// Gets the domain in the V direction. /// /// . - Interval DomainV { get; } + Interval DomainV + { + get; + } /// /// Compute a point at the specified surface coordinates. @@ -30,7 +36,7 @@ public interface ISurface /// /// Compute the normal at the specified surface coordinates. /// - /// U coordiante. + /// U coordinate. /// V coordinate. /// Normal vector. Vector3d NormalAt(double u, double v); @@ -38,9 +44,23 @@ public interface ISurface /// /// Compute the tangent plane at the specified surface coordinates. /// - /// U coordiante. + /// U coordinate. /// V coordinate. /// Tangent plane. Plane FrameAt(double u, double v); + + /// + /// Compute the distance between this surface and a point + /// + /// Point to compute distance to. + /// Number representing the distance. + double DistanceTo(Point3d point); + + /// + /// Compute the projection of a point on this surface. + /// + /// Point to compute distance to. + /// Projected 3d point on the surface. + Point3d ClosestPointTo(Point3d point); } } \ No newline at end of file diff --git a/src/Geometry/Intersect/Intersect.cs b/src/Geometry/Intersect/Intersect.cs index de0e72a..d225d27 100644 --- a/src/Geometry/Intersect/Intersect.cs +++ b/src/Geometry/Intersect/Intersect.cs @@ -1,10 +1,7 @@ using System; -using System.Collections; using System.Collections.Generic; -using Paramdigma.Core.Curves; using Paramdigma.Core.Geometry; using Paramdigma.Core.HalfEdgeMesh; -using Paramdigma.Core.LinearAlgebra; namespace Paramdigma.Core { @@ -26,14 +23,14 @@ public static ISLinePlane LinePlane(Line line, Plane plane, out Point3d intersec { Vector3d u = line.EndPoint - line.StartPoint; Vector3d w = line.StartPoint - plane.Origin; - + double d = Vector3d.DotProduct(plane.ZAxis, u); double n = -Vector3d.DotProduct(plane.ZAxis, w); - if (d <= 0.000001) + if (Math.Abs(d) <= Settings.Tolerance) { // Segment is parallel to plane - if (n == 0) + if (Math.Abs(n) < Settings.Tolerance) { // Segment lies in plane intersectionPoint = null; diff --git a/src/Geometry/SpatialStructures/PointCloud.cs b/src/Geometry/SpatialStructures/PointCloud.cs index 8f13e93..e737eda 100644 --- a/src/Geometry/SpatialStructures/PointCloud.cs +++ b/src/Geometry/SpatialStructures/PointCloud.cs @@ -1,7 +1,4 @@ -using System.Collections; using System.Collections.Generic; -using System.Drawing; -using Paramdigma.Core.Geometry; namespace Paramdigma.Core.SpatialSearch { diff --git a/src/Geometry/SpatialStructures/PointCloudMember.cs b/src/Geometry/SpatialStructures/PointCloudMember.cs index e4c7848..d446423 100644 --- a/src/Geometry/SpatialStructures/PointCloudMember.cs +++ b/src/Geometry/SpatialStructures/PointCloudMember.cs @@ -1,5 +1,3 @@ -using System.Collections; -using System.Collections.Generic; using System.Drawing; using Paramdigma.Core.Geometry; diff --git a/src/IO/OBJWritter.cs b/src/IO/OBJWritter.cs index 80c471e..99c4ce1 100644 --- a/src/IO/OBJWritter.cs +++ b/src/IO/OBJWritter.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections; - -#pragma warning disable 1591 +#pragma warning disable 1591 namespace Paramdigma.Core.IO { diff --git a/src/LinearAlgebra/Triplet.cs b/src/LinearAlgebra/Triplet.cs index 5230853..d6dfe01 100644 --- a/src/LinearAlgebra/Triplet.cs +++ b/src/LinearAlgebra/Triplet.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; #pragma warning disable 1591 diff --git a/src/Utility/Convert.cs b/src/Utility/Convert.cs index d9bd878..06f06b3 100644 --- a/src/Utility/Convert.cs +++ b/src/Utility/Convert.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using Paramdigma.Core.Curves; using Paramdigma.Core.Geometry; -using Paramdigma.Core.HalfEdgeMesh; -using Paramdigma.Core.LinearAlgebra; namespace Paramdigma.Core { diff --git a/src/Utility/Settings.cs b/src/Utility/Settings.cs index f83b01c..7a26d24 100644 --- a/src/Utility/Settings.cs +++ b/src/Utility/Settings.cs @@ -1,6 +1,4 @@ -using System; using System.IO; -using System.Linq; using System.Reflection; using Newtonsoft.Json; @@ -31,13 +29,13 @@ public static int MaxDecimals private static int tesselationLevel = 10; /// - /// Gets the default tesselation level when converting nurbs to meshes. + /// Gets the default tessellation level when converting nurbs to meshes. /// - /// Integer representing the default tesselation level. + /// Integer representing the default tessellation level. public static int GetDefaultTesselationLevel() => tesselationLevel; /// - /// Sets the default tesselation level when converting nurbs to meshes. + /// Sets the default tessellation level when converting nurbs to meshes. /// private static void SetDefaultTesselationLevel(int value) => tesselationLevel = value; @@ -75,5 +73,4 @@ private struct EmbeddedSettings public int DefaultTesselation; } } - } \ No newline at end of file diff --git a/tests/Collections/IntervalTests.cs b/tests/Collections/IntervalTests.cs index 6df637c..8be7799 100644 --- a/tests/Collections/IntervalTests.cs +++ b/tests/Collections/IntervalTests.cs @@ -7,12 +7,18 @@ namespace Paramdigma.Core.Tests public class IntervalTests { [Fact] - public async void CanCreate_Interval() + public void CanCreate_Interval() { var i0 = new Interval(0, 1); var i1 = Interval.Unit; var i2 = new Interval(i0); + + Assert.Equal(1, i0.Length); + Assert.Throws(() => new Interval(double.NaN,1)); + Assert.Throws(() => new Interval(0, double.NaN)); } + + [Fact] public void Can_CheckAndModifyDirection() diff --git a/tests/Extensions/ListExtensionsTests.cs b/tests/Extensions/ListExtensionsTests.cs new file mode 100644 index 0000000..c5ca0f1 --- /dev/null +++ b/tests/Extensions/ListExtensionsTests.cs @@ -0,0 +1,29 @@ +using Xunit; +using Paramdigma.Core.Extensions; +using Paramdigma.Core.Geometry; + +namespace Paramdigma.Core.Tests.Extensions +{ + public class ListExtensionsTests + { + [Fact] + public void CanCreate_RepeatedDefault() + { + for (int i = 1; i < 10; i++) + { + var list2 = Lists.RepeatedDefault(i); + Assert.True(list2.Count == i); + } + } + + [Fact] + public void CanCreate_Repeated() + { + for (int i = 1; i < 10; i++) + { + var list2 = Lists.Repeated(new Point3d(1,1,1),4 ); + list2.ForEach(pt => Assert.Equal(new Point3d(1,1,1),pt)); + } + } + } +} \ No newline at end of file diff --git a/tests/Geometry/2D/Polyline2dTests.cs b/tests/Geometry/2D/Polyline2dTests.cs index e814e63..0b128c6 100644 --- a/tests/Geometry/2D/Polyline2dTests.cs +++ b/tests/Geometry/2D/Polyline2dTests.cs @@ -1,4 +1,3 @@ -using System; using System.Collections; using System.Collections.Generic; using Paramdigma.Core.Geometry; diff --git a/tests/Geometry/2D/Ray2dTests.cs b/tests/Geometry/2D/Ray2dTests.cs new file mode 100644 index 0000000..f503807 --- /dev/null +++ b/tests/Geometry/2D/Ray2dTests.cs @@ -0,0 +1,20 @@ +using System; +using Paramdigma.Core.Geometry; +using Xunit; + +namespace Paramdigma.Core.Tests.Geometry +{ + public class Ray2dTests + { + [Fact] + public void CanCreate_AndThrowExceptions() + { + var ray = new Ray2d(Point2d.Origin, Vector2d.WorldX); + Assert.Equal(Point2d.Origin, ray.Origin); + Assert.Equal(Vector2d.WorldX, ray.Direction); + + Assert.Throws(() => new Ray2d(Point2d.Origin, null)); + Assert.Throws(() => new Ray2d(null, Vector2d.WorldY)); + } + } +} \ No newline at end of file diff --git a/tests/Geometry/3D/Intersect3dTests.cs b/tests/Geometry/3D/Intersect3dTests.cs new file mode 100644 index 0000000..308fdf5 --- /dev/null +++ b/tests/Geometry/3D/Intersect3dTests.cs @@ -0,0 +1,76 @@ +using Paramdigma.Core.Geometry; +using Xunit; + +namespace Paramdigma.Core.Tests.Geometry._3D +{ + public class Intersect3dTests + { + [Fact] + public void CanIntersect_Line_Line() + { + var lineA = new Line(Point3d.WorldOrigin, new Point3d(1, 1, 1)); + var lineB = new Line(new Point3d(1, 0, 0), new Point3d(0, 1, 1)); + + var status = Intersect3D.LineLine(lineA, lineB, out Intersect3D.IRLineLine result); + Assert.Equal(Intersect3D.ISLineLine.Point, status); + Assert.Equal(new Point3d(0.5, 0.5, 0.5), result.PointA); + Assert.Equal(new Point3d(0.5, 0.5, 0.5), result.PointB); + Assert.Equal(0.5,result.TA); + Assert.Equal(0.5,result.TB); + } + + + [Fact] + public void CanIntersect_Line_Plane() + { + var expected = new Point3d(.4, .23, 0); + var a = new Point3d(.4, .23, 1); + var b = new Point3d(.4, .23, -1); + var lineA = new Line(a, b); + var plane = Plane.WorldXY; + + var status = Intersect3D.LinePlane(lineA, plane, out Point3d actual); + Assert.Equal(Intersect3D.ISLinePlane.Point, status); + Assert.Equal(expected, actual); + } + + [Fact] + public void CannotIntersect_Line_Plane_Parallel() + { + var a = new Point3d(.6, .6, 0); + var b = new Point3d(.4, .2, 0); + var lineA = new Line(a, b); + var plane = Plane.WorldXY; + + var status = Intersect3D.LinePlane(lineA, plane, out Point3d actual); + Assert.Equal(Intersect3D.ISLinePlane.OnPlane, status); + Assert.Null(actual); + } + + [Fact] + public void CannotIntersect_Line_Plane_DoNotTouch() + { + var a = new Point3d(.4, .23, 1); + var b = new Point3d(.4, .23, .2); + var lineA = new Line(a, b); + var plane = Plane.WorldXY; + + var status = Intersect3D.LinePlane(lineA, plane, out Point3d actual); + Assert.Equal(Intersect3D.ISLinePlane.NoIntersection, status); + Assert.Null(actual); + } + + [Fact] + public void CannotIntersect_Line_Plane_DoNotTouchAndAreParallel() + { + var a = new Point3d(.4, .23, 1); + var b = new Point3d(.9, .23, 1); + var lineA = new Line(a, b); + var plane = Plane.WorldXY; + + var status = Intersect3D.LinePlane(lineA, plane, out Point3d actual); + Assert.Equal(Intersect3D.ISLinePlane.NoIntersection, status); + Assert.Null(actual); + } + } +} \ No newline at end of file diff --git a/tests/Geometry/3D/LineTests.cs b/tests/Geometry/3D/LineTests.cs index 323dd28..bd44045 100644 --- a/tests/Geometry/3D/LineTests.cs +++ b/tests/Geometry/3D/LineTests.cs @@ -1,6 +1,4 @@ using System; -using System.Collections; -using System.Collections.Generic; using Paramdigma.Core.Geometry; using Xunit; diff --git a/tests/Geometry/3D/NurbsTests.cs b/tests/Geometry/3D/NurbsTests.cs index 905cd10..f549dfb 100644 --- a/tests/Geometry/3D/NurbsTests.cs +++ b/tests/Geometry/3D/NurbsTests.cs @@ -61,10 +61,6 @@ public void NurbsCurvePoint_Works() var pt = NurbsCalculator.CurvePoint(3, 1, u, new Point3d[] { p0, p1, p2, p3 }, (double)i / n); var pt2 = NurbsCalculator.CurvePoint(3, 2, u2, new Point3d[] { p0, p1, p2, p3 }, (double)i / n); var pt3 = NurbsCalculator.CurvePoint(3, 3, u3, new Point3d[] { p0, p1, p2, p3 }, (double)i / n); - Console.WriteLine($"t: {(double)i / n} — {pt}"); - Console.WriteLine($"t: {(double)i / n} — {pt2}"); - Console.WriteLine($"t: {(double)i / n} — {pt3}"); - Console.WriteLine("---"); } watch.Stop(); Console.WriteLine(watch.Elapsed); diff --git a/tests/Geometry/3D/PlaneTests.cs b/tests/Geometry/3D/PlaneTests.cs index b3c40b9..9ec1916 100644 --- a/tests/Geometry/3D/PlaneTests.cs +++ b/tests/Geometry/3D/PlaneTests.cs @@ -67,5 +67,46 @@ public void CanCompute_ClosestPoint() Assert.True(result == expected); Assert.True(dist == 1); } + + [Fact] + public void LinearPoints_ThrowError() + { + var ptA = new Point3d(0, 0, 0); + var ptB = new Point3d(0.5, 0.5, 0.5); + var ptC = new Point3d(1, 1, 1); + + Assert.Throws(() => new Plane(ptA, ptB, ptC)); + } + + [Fact] + public void CanRemap_ToXYPlane() + { + var yz = Plane.WorldYZ; + var pt = new Point3d(1, 1, 0); + var expected = new Point3d(0, 1, 1); + var result = yz.RemapToWorldXYSpace(pt); + Assert.Equal(expected, result); + } + + [Fact] + public void CanRemap_FromXYPlane() + { + var yz = Plane.WorldYZ; + var pt = new Point3d(0, 1, 1); + var expected = new Point3d(1, 1, 0); + var result = yz.RemapToPlaneSpace(pt); + Assert.Equal(expected, result); + } + + [Fact] + public void CanBe_Flipped() + { + var xy = Plane.WorldXY; + var flipped = xy.Clone(); + flipped.Flip(); + Assert.Equal(xy.XAxis, flipped.YAxis); + Assert.Equal(xy.YAxis, flipped.XAxis); + Assert.Equal(xy.ZAxis, -flipped.ZAxis); + } } } \ No newline at end of file diff --git a/tests/Geometry/3D/Point3dTests.cs b/tests/Geometry/3D/Point3dTests.cs index 4328600..95b31f3 100644 --- a/tests/Geometry/3D/Point3dTests.cs +++ b/tests/Geometry/3D/Point3dTests.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Paramdigma.Core.Geometry; using Xunit; @@ -119,6 +120,40 @@ public void CanConvert_ToArray() Assert.True(arr.Length == 3); Assert.True(arr[0] == 1 && arr[1] == 0 && arr[2] == 0); } - + + [Theory] + [MemberData(nameof(UnsetPointData))] + public void CanToggleUnset_WithX(BasePoint pt) + { + Assert.True(pt.IsUnset); + pt.X += 1; + Assert.False(pt.IsUnset); + + } + + [Theory] + [MemberData(nameof(UnsetPointData))] + public void CanToggleUnset_WithY(BasePoint pt) + { + Assert.True(pt.IsUnset); + pt.Y += 1; + Assert.False(pt.IsUnset); + + } + + [Theory] + [MemberData(nameof(UnsetPointData))] + public void CanToggleUnset_WithZ(BasePoint pt) + { + Assert.True(pt.IsUnset); + pt.Z += 1; + Assert.False(pt.IsUnset); + } + + public static IEnumerable UnsetPointData => new List { + new object[] { new Point3d() }, + new object[] { Point3d.Unset }, + new object[] { new Point4d() }, + }; } } diff --git a/tests/Geometry/3D/Point4dTests.cs b/tests/Geometry/3D/Point4dTests.cs new file mode 100644 index 0000000..4e0d9b8 --- /dev/null +++ b/tests/Geometry/3D/Point4dTests.cs @@ -0,0 +1,138 @@ +using Paramdigma.Core.Geometry; +using Xunit; + +namespace Paramdigma.Core.Tests.Geometry +{ + public class Point4dTests + { + [Fact] + public void CanBe_Created() + { + var pt3 = new Point3d(1, 0, 0); + var pt4 = new Point4d(1, 0, 0, 1); + var pt42 = new Point4d(pt3); + + Assert.Equal(pt42, pt4); + } + + [Fact] + public void CanBe_Added() + { + const double a = 3.3; + const double b = 2.2; + const double c = 4.11; + const double d = 1.344; + + var ptA = new Point4d(a, b, c, d); + var ptB = new Point4d(b, c, a, d); + var ptResult = new Point4d(a + b, b + c, c + a, d + d); + Assert.True(ptA + ptB == ptResult); + } + + [Fact] + public void CanToggle_IsUnset_OnWeightChange() + { + var pt = new Point4d(); + Assert.True(pt.IsUnset); + pt.Weight += 1; + Assert.False(pt.IsUnset); + } + [Fact] + public void CanBe_Added_WithVector() + { + const double a = 3.3; + const double b = 2.2; + const double c = 4.11; + const double d = 1.344; + + var ptA = new Point4d(a, b, c, d); + var v = new Vector3d(b, c, a); + var ptResult = new Point4d(a + b, b + c, c + a, d); + Assert.True(ptA + v == ptResult); + } + + [Fact] + public void CanBe_Subtracted() + { + const double a = 3.3; + const double b = 2.2; + const double c = 4.11; + const double d = 1.344; + + var ptA = new Point4d(a, b, c, d); + var ptB = new Point4d(b, c, a, d); + var expected = new Point4d(a - b, b - c, c - a, d - d); + var actual = ptA - ptB; + Assert.Equal(expected, actual); + } + + [Fact] + public void CanBe_Multiplied() + { + const double a = 3.3; + const double b = 2.2; + const double c = 4.11; + const double d = 1.344; + const double m = 1.45; + var ptA = new Point4d(a, b, c, d); + var ptResult = new Point4d(a * m, b * m, c * m, d * m); + Assert.True(ptA * m == ptResult); + Assert.True(m * ptA == ptResult); + } + + [Fact] + public void CanBe_Divided() + { + const double a = 3.3; + const double b = 2.2; + const double c = 4.11; + const double d = 1.344; + const double m = 1.45; + var ptA = new Point4d(a, b, c, d); + var ptResult = new Point4d(a / m, b / m, c / m, d / m); + Assert.True(ptA / m == ptResult); + } + + [Fact] + public void CanBe_Negated() + { + const double a = 3.3; + const double b = 2.2; + const double c = 4.11; + const double d = 1.344; + + var ptA = new Point4d(a, b, c, d); + var ptResult = new Point4d(-a, -b, -c, d); + Assert.True(-ptA == ptResult); + } + + [Fact] + public void CanCheck_Equality() + { + const double a = 3.3; + const double b = 2.2; + const double c = 4.11; + const double d = 1.344; + + var ptA = new Point4d(a, b, c, d); + var expectedEqual = new Point4d(a + (Settings.Tolerance / 2), b, c, d); + var expectedNotEqual = new Point4d(a + (Settings.Tolerance * 2), b, c, d); + Assert.True(ptA == expectedEqual); + Assert.Equal(ptA.GetHashCode(), expectedEqual.GetHashCode()); + Assert.True(ptA != expectedNotEqual); + Assert.NotEqual(ptA.GetHashCode(), expectedNotEqual.GetHashCode()); + Assert.False(ptA == null); + + } + + [Fact] + public void CanCreate_FromPointAndWeight() + { + var pt = new Point3d(1, 0, 1); + const int weight = 1; + var pt4 = new Point4d(pt, weight); + Assert.Equal(pt, pt4.Position); + Assert.Equal(weight, pt4.Weight); + } + } +} \ No newline at end of file diff --git a/tests/Geometry/3D/Polyline3dTests.cs b/tests/Geometry/3D/Polyline3dTests.cs new file mode 100644 index 0000000..f2da894 --- /dev/null +++ b/tests/Geometry/3D/Polyline3dTests.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using Paramdigma.Core.Exceptions; +using Paramdigma.Core.Geometry; +using Xunit; + +namespace Paramdigma.Core.Tests.Geometry +{ + public class Polyline3dTests : CurveBaseTests + { + private Polyline getTestPolyline(int count) + { + var knots = new List(); + for (int i = 0; i <= count; i++) + { + knots.Add(new Point3d(i, 0, 0)); + } + + return new Polyline(knots); + } + + [Fact] + public override void CanGet_Length() + { + for (int i = 3; i < 4; i++) + { + var poly = this.getTestPolyline(i); + var length = poly.Length; + Assert.True(length == i, $"Length {length} is not {i}"); + } + } + + [Fact] + public void CanEnumerate_Knots() + { + var poly = this.getTestPolyline(3); + foreach (var knot in poly) + { + Assert.NotNull(knot); + } + } + + [Fact] + public override void CanGet_PointAt() + { + var poly = this.getTestPolyline(3); + var pt = poly.PointAt(1); + var expected = new Point3d(1, 0, 0); + Assert.Equal(expected, pt); + } + + [Fact] + public void CanAdd_Knot() + { + var poly = this.getTestPolyline(3); + var segCount = poly.Segments.Count; + var knotCount = poly.Knots.Count; + poly.AddKnot(poly[0]); + Assert.Equal(segCount + 1, poly.Segments.Count); + Assert.Equal(knotCount + 1, poly.Knots.Count); + } + + [Fact] + public void CanInsert_Knot() + { + var poly = this.getTestPolyline(3); + var segCount = poly.Segments.Count; + var knotCount = poly.Knots.Count; + poly.InsertKnot(poly[0],2); + Assert.Equal(segCount + 1, poly.Segments.Count); + Assert.Equal(knotCount + 1, poly.Knots.Count); + Assert.Equal(poly[0],poly[2]); + } + + [Fact] + public void CanRemove_KnotAtIndex() + { + var poly = this.getTestPolyline(3); + var segCount = poly.Segments.Count; + var knotCount = poly.Knots.Count; + poly.RemoveKnotAt(0); + + Assert.Equal(segCount - 1, poly.Segments.Count); + Assert.Equal(knotCount - 1, poly.Knots.Count); + } + + [Fact] + public void CanRemove_Knot() + { + var poly = this.getTestPolyline(3); + var segCount = poly.Segments.Count; + var knotCount = poly.Knots.Count; + var unexpected = new Point3d(poly[0]); + poly.RemoveKnot(poly[0]); + + Assert.Equal(segCount - 1, poly.Segments.Count); + Assert.Equal(knotCount - 1, poly.Knots.Count); + Assert.NotEqual(unexpected,poly[0]); + } + + [Fact] + public void CanRemove_Knot_ThrowsException() + { + var poly = this.getTestPolyline(3); + var poly2 = new Polyline(); + Assert.Throws(() => poly.RemoveKnotAt(17)); + Assert.Throws(() => poly2.RemoveKnotAt(0)); + } + + [Fact] + public void CanToggle_IsClosed() + { + var poly = this.getTestPolyline(3); + Assert.False(poly.IsClosed); + poly.AddKnot(poly[0]); + Assert.True(poly.IsClosed); + poly.RemoveKnotAt(4); + Assert.False(poly.IsClosed); + } + + [Fact] + public override void CanCheck_Validity() + { + var poly = this.getTestPolyline(3); + Assert.True(poly.IsValid); + poly = new Polyline(); + Assert.False(poly.IsValid); + + var pt = new Point3d(0, 0, 0); + poly = new Polyline(new List {pt, pt}); + Assert.False(poly.IsValid); + } + + [Fact] + public void CanAccess_UpdatedSegmentList() + { + var poly = this.getTestPolyline(3); + Assert.Equal(3,poly.Segments.Count); + poly.AddKnot(poly[0]); + Assert.Equal(4,poly.Segments.Count); + + } + + [Fact] + public override void CanGet_Tangent() + { + //TODO: Improve test + var poly = this.getTestPolyline(3); + Assert.NotNull(poly.TangentAt(1)); + } + + [Fact] + public override void CanGet_Normal() + { + //TODO: Improve test + var poly = this.getTestPolyline(3); + Assert.NotNull(poly.NormalAt(1)); + } + + [Fact] + public override void CanGet_BiNormal() + { + //TODO: Improve test + var poly = this.getTestPolyline(3); + Assert.NotNull(poly.BinormalAt(1)); + } + + [Fact] + public override void CanGet_PerpFrame() + { + //TODO: Improve test + var poly = this.getTestPolyline(3); + Assert.NotNull(poly.FrameAt(1)); + } + } +} \ No newline at end of file diff --git a/tests/Geometry/3D/Ray3dTests.cs b/tests/Geometry/3D/Ray3dTests.cs new file mode 100644 index 0000000..2bf9722 --- /dev/null +++ b/tests/Geometry/3D/Ray3dTests.cs @@ -0,0 +1,29 @@ +using System; +using Paramdigma.Core.Geometry; +using Xunit; + +namespace Paramdigma.Core.Tests.Geometry +{ + public class Ray3dTests + { + [Fact] + public void CanCreate_AndThrowExceptions() + { + var ray = new Ray(Point3d.WorldOrigin, Vector3d.UnitX); + Assert.Equal(Point3d.WorldOrigin, ray.Origin); + Assert.Equal(Vector3d.UnitX, ray.Direction); + + Assert.Throws(() => new Ray(Point3d.WorldOrigin, null)); + Assert.Throws(() => new Ray(null, Vector3d.UnitY)); + } + + [Fact] + public void CanCompute_PointAt() + { + var ray = new Ray(Point3d.WorldOrigin, Vector3d.UnitX); + var expected = new Point3d(3, 0, 0); + var actual = ray.PointAt(3); + Assert.Equal(expected, actual); + } + } +} \ No newline at end of file diff --git a/tests/Utilities/ResourcesTests.cs b/tests/Utilities/ResourcesTests.cs index 0fa432e..78b7050 100644 --- a/tests/Utilities/ResourcesTests.cs +++ b/tests/Utilities/ResourcesTests.cs @@ -1,28 +1,31 @@ using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using System.Globalization; using Xunit; -using Microsoft.Extensions.FileProviders; +using Xunit.Abstractions; namespace Paramdigma.Core.Utilities { public class ResourcesTests { + public ResourcesTests( ITestOutputHelper testOutputHelper ) + { + this.testOutputHelper = testOutputHelper; + } + + private readonly ITestOutputHelper testOutputHelper; + [Fact] public void ResetSettingsValues_FromResource() { - Console.WriteLine(Settings.Tolerance); - Console.WriteLine(Settings.MaxDecimals); - Console.WriteLine(Settings.GetDefaultTesselationLevel()); + testOutputHelper.WriteLine(Settings.Tolerance.ToString( CultureInfo.CurrentCulture )); + testOutputHelper.WriteLine(Settings.MaxDecimals.ToString()); + testOutputHelper.WriteLine(Settings.GetDefaultTesselationLevel().ToString()); Settings.SetTolerance(0.1); - Console.WriteLine(Settings.Tolerance); - Console.WriteLine(Settings.MaxDecimals); + testOutputHelper.WriteLine(Settings.Tolerance.ToString( CultureInfo.CurrentCulture )); + testOutputHelper.WriteLine(Settings.MaxDecimals.ToString()); Settings.Reset(); - Console.WriteLine(Settings.Tolerance); - Console.WriteLine(Settings.MaxDecimals); + testOutputHelper.WriteLine(Settings.Tolerance.ToString( CultureInfo.CurrentCulture )); + testOutputHelper.WriteLine(Settings.MaxDecimals.ToString()); } } } \ No newline at end of file