diff --git a/.editorconfig b/.editorconfig
index 0525433..47356ca 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -5,51 +5,12 @@ root = true
[*]
indent_style = tab
insert_final_newline = true
-
-# Build scripts
-[*.{yml,yaml}]
-indent_style = spaces
-indent_size = 2
-
-# Code files
-[*.{cs,csx,vb,vbx}]
+tab_width = 4
indent_size = 4
-charset = utf-8-bom
-
-# XML project files
-[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
-indent_size = 2
-
-# Dotnet code style settings:
-[*.{cs,tt}]
-
-# IDE0055: Fix formatting
-dotnet_diagnostic.IDE0055.severity = warning
-
-# Sort using and Import directives with System.* appearing first
-dotnet_sort_system_directives_first = true
-dotnet_separate_import_directive_groups = false
-
-# require accessibility modifiers
-dotnet_style_require_accessibility_modifiers = for_non_interface_members:error
-
-# Avoid "this." and "Me." if not necessary
-dotnet_style_qualification_for_field = false:refactoring
-dotnet_style_qualification_for_property = false:refactoring
-dotnet_style_qualification_for_method = false:refactoring
-dotnet_style_qualification_for_event = false:refactoring
+charset = utf-8
-# Use language keywords instead of framework type names for type references
-dotnet_style_predefined_type_for_locals_parameters_members = true:error
-dotnet_style_predefined_type_for_member_access = true:suggestion
-# Suggest more modern language features when available
-dotnet_style_object_initializer = true:suggestion
-dotnet_style_collection_initializer = true:suggestion
-dotnet_style_coalesce_expression = true:suggestion
-dotnet_style_null_propagation = true:suggestion
-dotnet_style_explicit_tuple_names = true:suggestion
-dotnet_style_prefer_compound_assignment = true:warning
+### Naming rules: ###
# Constants are PascalCase
dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion
@@ -83,10 +44,10 @@ dotnet_naming_symbols.non_private_static_fields.required_modifiers = static
dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case
-# Static fields are camelCase and start with s_
-dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion
-dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields
-dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style
+# Static fields are s_camelCase
+dotnet_naming_rule.static_fields_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.static_fields_should_be_pascal_case.symbols = static_fields
+dotnet_naming_rule.static_fields_should_be_pascal_case.style = static_field_style
dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_symbols.static_fields.required_modifiers = static
@@ -131,8 +92,81 @@ dotnet_naming_symbols.all_members.applicable_kinds = *
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
-# CSharp code style settings:
+
+### Dotnet code style settings: ###
+
+# Sort using and Import directives with System.* appearing first
+dotnet_sort_system_directives_first = true
+dotnet_separate_import_directive_groups = false
+
+# require accessibility modifiers
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:error
+
+# Avoid "this." and "Me." if not necessary
+dotnet_style_qualification_for_field = false:refactoring
+dotnet_style_qualification_for_property = false:refactoring
+dotnet_style_qualification_for_method = false:refactoring
+dotnet_style_qualification_for_event = false:refactoring
+
+# Use language keywords instead of framework type names for type references
+dotnet_style_predefined_type_for_locals_parameters_members = true:error
+dotnet_style_predefined_type_for_member_access = true:suggestion
+
+# Initializers
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_prefer_collection_expression = when_types_loosely_match:warning
+
+# Null checks
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
+
+# Tuple Naming
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+
+# Assignments
+dotnet_style_prefer_conditional_expression_over_assignment = true:error
+dotnet_style_prefer_conditional_expression_over_return = true:none
+dotnet_style_prefer_compound_assignment = true:warning
+
+# Parenthesis
+dotnet_code_quality_unused_parameters = all:suggestion
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion
+
+# Miscellaneous
+dotnet_style_prefer_auto_properties = true:warning
+dotnet_style_prefer_simplified_boolean_expressions = true:warning
+dotnet_style_prefer_simplified_interpolation = true:warning
+dotnet_style_namespace_match_folder = true:warning
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_readonly_field = true:warning
+
+# New-line preferences
+dotnet_style_allow_multiple_blank_lines_experimental = false:warning
+dotnet_style_allow_statement_immediately_after_block_experimental = false:warning
+csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = false:warning
+csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false:warning
+
+# Build scripts
+[*.{yml,yaml}]
+indent_style = spaces
+indent_size = 2
+
+# XML project files
+[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
+indent_size = 2
+
+# Code files
[*.cs]
+
+## C# style settings:
+
# Newline settings
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
@@ -156,25 +190,18 @@ csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion
# Prefer method-like constructs to have a block body
-csharp_style_expression_bodied_methods = true:suggestion
+csharp_style_expression_bodied_methods = false:none
csharp_style_expression_bodied_constructors = false:none
csharp_style_expression_bodied_operators = false:none
+# Prefer local method constructs to have a block body
+csharp_style_expression_bodied_local_functions = true:suggestion
+
# Prefer property-like constructs to have an expression-body
csharp_style_expression_bodied_properties = true:suggestion
csharp_style_expression_bodied_indexers = true:suggestion
csharp_style_expression_bodied_accessors = true:suggestion
-# Suggest more modern language features when available
-csharp_style_pattern_matching_over_is_with_cast_check = true:warning
-csharp_style_pattern_matching_over_as_with_null_check = true:warning
-csharp_style_inlined_variable_declaration = true:warning
-csharp_style_throw_expression = true:warning
-csharp_style_conditional_delegate_call = true:warning
-csharp_style_prefer_switch_expression = true:warning
-csharp_prefer_simple_using_statement = true:suggestion
-csharp_style_namespace_declarations = file_scoped:error
-
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
@@ -200,24 +227,73 @@ csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Blocks are allowed
-csharp_prefer_braces = true:silent
+csharp_prefer_braces = when_multiline:silent
csharp_preserve_single_line_blocks = true:silent
csharp_preserve_single_line_statements = true:silent
+# Pattern Matching
+csharp_style_prefer_pattern_matching = true:warning
+csharp_style_prefer_not_pattern = true:warning
+csharp_style_prefer_extended_property_pattern = true:warning
+csharp_style_pattern_matching_over_as_with_null_check = true:warning
+csharp_style_pattern_matching_over_is_with_cast_check = true:warning
+
+# Namespace
+csharp_style_namespace_declarations = file_scoped:error
+csharp_using_directive_placement = outside_namespace:warning
+
+# Suggest more modern language features when available
+csharp_prefer_simple_default_expression = true:warning
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_prefer_static_local_function = true:suggestion
+csharp_style_conditional_delegate_call = true:warning
+csharp_style_deconstructed_variable_declaration = true:warning
+csharp_style_expression_bodied_lambdas = true:suggestion
+csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
+csharp_style_inlined_variable_declaration = true:warning
+csharp_style_prefer_index_operator = true:suggestion
+csharp_style_prefer_local_over_anonymous_function = true:suggestion
+csharp_style_prefer_method_group_conversion = true:silent
+csharp_style_prefer_null_check_over_type_check = true:suggestion
+csharp_style_prefer_primary_constructors = true:warning
+csharp_style_prefer_range_operator = true:suggestion
+csharp_style_prefer_readonly_struct = true:suggestion
+csharp_style_prefer_readonly_struct_member = true:suggestion
+csharp_style_prefer_switch_expression = true:warning
+csharp_style_prefer_top_level_statements = true:silent
+csharp_style_prefer_tuple_swap = true:suggestion
+csharp_style_prefer_utf8_string_literals = true:suggestion
+csharp_style_throw_expression = true:warning
+csharp_style_unused_value_assignment_preference = discard_variable:warning
+csharp_style_unused_value_expression_statement_preference = discard_variable:warning
+
+# New Lines
+csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
+csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent
+csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
+csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent
+csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent
+
+# Style Analytics
+dotnet_analyzer_diagnostic.category-Style.severity = warning
+
+# XML Documentation
dotnet_diagnostic.CS0105.severity = error # CS0105: Using directive is unnecessary.
dotnet_diagnostic.CS1573.severity = error # CS1573: Missing XML comment for parameter
dotnet_diagnostic.CS1591.severity = error # CS1591: Missing XML comment for publicly visible type or member
+dotnet_diagnostic.CS1712.severity = error # CS1712: Type parameter has no matching typeparam tag in the XML comment (but other type parameters do)
+# Async
dotnet_diagnostic.CS1998.severity = error # CS1998: Async method lacks 'await' operators and will run synchronously
dotnet_diagnostic.CS4014.severity = error # CS4014: Because this call is not awaited, execution of the current method continues before the call is completed
+dotnet_diagnostic.CA2007.severity = none # CA2007: Consider calling ConfigureAwait on the awaited task
+
+# Dispose things need disposing
+dotnet_diagnostic.CA2000.severity = error # CA2000: Dispose objects before losing scope
-dotnet_diagnostic.CS8602.severity = error # CS8602: Dereference of a possibly null reference.
-dotnet_diagnostic.CS8603.severity = error # CS8603: Possible null reference return
-dotnet_diagnostic.CS8604.severity = error # CS8604: Possible null reference argument
-dotnet_diagnostic.CS8618.severity = error # CS8618: Non-nullable field is uninitialized. Consider declaring as nullable.
+dotnet_diagnostic.CA1034.severity = none # CA1034: Nested types should not be visible
+dotnet_diagnostic.CA1515.severity = none # CA1515: Consider making public types internal
+dotnet_diagnostic.CA1724.severity = none
-dotnet_diagnostic.MA0016.severity = none # MA0016: Prefer returning collection abstraction instead of implementation
-dotnet_diagnostic.MA0048.severity = none # MA0048: File name must match type name
-dotnet_diagnostic.MA0049.severity = none # MA0049: Type name should not match containing namespace
-dotnet_diagnostic.MA0051.severity = none # MA0051: Method is too long
-dotnet_diagnostic.MA0071.severity = silent # MA0071: Avoid using redundant else
+dotnet_diagnostic.MA0049.severity = none
+dotnet_diagnostic.IDE0305.severity = none
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7c4252b..5cbcb13 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,6 +1,14 @@
name: Build
-on: [push, pull_request]
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - 'master'
+ paths-ignore:
+ - '**/readme.md'
+ pull_request:
+ types: [opened, synchronize, reopened]
jobs:
build:
@@ -8,43 +16,25 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
with:
- languages: 'csharp'
-
- - name: Setup .NET 2.2
- uses: actions/setup-dotnet@v2
- with:
- dotnet-version: '2.2.x'
- - name: Setup .NET 3.1
- uses: actions/setup-dotnet@v2
- with:
- dotnet-version: '3.1.x'
- - name: Setup .NET 6.0
- uses: actions/setup-dotnet@v2
- with:
- dotnet-version: '6.0.x'
+ dotnet-version: |
+ 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build -c Release --no-restore
- name: Test
- run: dotnet test -c Release --no-build --verbosity normal
+ run: dotnet test -c Release --no-build --logger GitHubActions
- - name: Test Report
- uses: dorny/test-reporter@v1
- if: success() || failure() # run this step even if previous step failed
+ - name: Upload coverage reports to Codecov with GitHub Action
+ uses: codecov/codecov-action@v4
with:
- name: 'Test report'
- path: '**/*.trx'
- reporter: 'dotnet-trx'
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ token: ${{ secrets.CODECOV_TOKEN }}
- name: Package
run: dotnet pack -c Release --no-build -o nupkgs
diff --git a/Directory.Build.props b/Directory.Build.props
index c873469..b52086b 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,16 +1,23 @@
-
- latest
- enable
- enable
- latest-recommended
- true
-
+
+ latest
+ net8.0
-
- true
- true
- trx%3bLogFileName=$(MSBuildProjectName)-$(TargetFramework).trx
- $(MSBuildThisFileDirectory)/TestResults
-
+ enable
+ $(WarningsAsErrors);nullable;
+
+ enable
+
+ latest-all
+ true
+
+ true
+
+
+
+ true
+ true
+ true
+ opencover
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
new file mode 100644
index 0000000..a297d1d
--- /dev/null
+++ b/Directory.Packages.props
@@ -0,0 +1,21 @@
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RBush.Test/.editorconfig b/RBush.Test/.editorconfig
new file mode 100644
index 0000000..345b270
--- /dev/null
+++ b/RBush.Test/.editorconfig
@@ -0,0 +1,9 @@
+[*.cs]
+
+dotnet_diagnostic.CS1573.severity = none # CS1573: Missing XML comment for parameter
+dotnet_diagnostic.CS1591.severity = none # CS1591: Missing XML comment for publicly visible type or member
+dotnet_diagnostic.CS1712.severity = none # CS1712: Type parameter has no matching typeparam tag in the XML comment (but other type parameters do)
+
+dotnet_diagnostic.CA1707.severity = none # CA1707: Identifiers should not contain underscores
+dotnet_diagnostic.CA1814.severity = none
+dotnet_diagnostic.CA1822.severity = none # CA1822: Mark members as static
diff --git a/RBush.Test/KnnTests.cs b/RBush.Test/KnnTests.cs
index 9bb8c42..d0a425d 100644
--- a/RBush.Test/KnnTests.cs
+++ b/RBush.Test/KnnTests.cs
@@ -1,4 +1,4 @@
-using Xunit;
+using Xunit;
namespace RBush.Test;
@@ -22,7 +22,7 @@ public class KnnTests
{99,3,103,5}, {41,92,44,96}, {79,40,79,41}, {29,2,29,4},
});
- [Fact]
+ [Test]
public void FindsNNeighbors()
{
var bush = new RBush();
@@ -35,19 +35,19 @@ public void FindsNNeighbors()
Assert.Equal(expected, result);
}
- [Fact]
+ [Test]
public void DoesNotThrowIfRequestingTooManyItems()
{
var bush = new RBush();
bush.BulkLoad(s_points);
- bush.Knn(1000, 40, 40);
+ _ = bush.Knn(1000, 40, 40);
}
///
/// This test is not correct in original javascript library
///
- [Fact]
+ [Test]
public void FindAllNeighborsForMaxDistance()
{
var bush = new RBush();
@@ -61,7 +61,7 @@ public void FindAllNeighborsForMaxDistance()
Assert.Equal(expected, result);
}
- [Fact]
+ [Test]
public void FindNNeighborsForMaxDistance()
{
var bush = new RBush();
@@ -76,13 +76,13 @@ public void FindNNeighborsForMaxDistance()
Assert.Equal(expected, result);
}
- [Fact]
+ [Test]
public void DoesNotThrowIfRequestingTooManyItemsForMaxDistance()
{
var bush = new RBush();
bush.BulkLoad(s_points);
- bush.Knn(1000, 40, 40, maxDistance: 10);
+ _ = bush.Knn(1000, 40, 40, maxDistance: 10);
}
private static readonly Point[] s_richData = Point.CreatePoints(
@@ -92,7 +92,7 @@ public void DoesNotThrowIfRequestingTooManyItemsForMaxDistance()
{ 4, 2, 4, 2 }, { 2, 4, 2, 4 }, { 5, 3, 5, 3 },
});
- [Fact]
+ [Test]
public void FindNeighborsThatSatisfyAGivenPredicate()
{
var bush = new RBush();
diff --git a/RBush.Test/Point.cs b/RBush.Test/Point.cs
index 90d5e2c..5f590a6 100644
--- a/RBush.Test/Point.cs
+++ b/RBush.Test/Point.cs
@@ -1,17 +1,14 @@
-namespace RBush.Test;
+namespace RBush.Test;
-internal sealed class Point : ISpatialData, IEquatable
+internal sealed class Point(double minX, double minY, double maxX, double maxY) : ISpatialData, IEquatable
{
- private readonly Envelope _envelope;
-
- public Point(double minX, double minY, double maxX, double maxY)
- {
- _envelope = new Envelope(
+ private readonly Envelope _envelope =
+ new(
MinX: minX,
MinY: minY,
MaxX: maxX,
- MaxY: maxY);
- }
+ MaxY: maxY
+ );
public ref readonly Envelope Envelope => ref _envelope;
diff --git a/RBush.Test/RBush.Test.csproj b/RBush.Test/RBush.Test.csproj
index 65c42db..f05d7f4 100644
--- a/RBush.Test/RBush.Test.csproj
+++ b/RBush.Test/RBush.Test.csproj
@@ -1,13 +1,16 @@
-
+
- netcoreapp2.2;netcoreapp3.1;net6.0
+ net8.0
+ false
-
-
-
+
+
+
+
+
diff --git a/RBush.Test/RBushTests.cs b/RBush.Test/RBushTests.cs
index 68aeb01..a8b5fad 100644
--- a/RBush.Test/RBushTests.cs
+++ b/RBush.Test/RBushTests.cs
@@ -1,4 +1,4 @@
-using Xunit;
+using Xunit;
namespace RBush.Test;
@@ -24,10 +24,10 @@ private static List GetPoints(int cnt) =>
maxY: i))
.ToList();
- [Fact]
+ [Test]
public void RootLeafSplitWorks()
{
- var data = RBushTests.GetPoints(12);
+ var data = GetPoints(12);
var tree = new RBush();
for (var i = 0; i < 9; i++)
@@ -54,7 +54,7 @@ public void RootLeafSplitWorks()
Assert.Equal(9, tree.Root.Envelope.MaxY);
}
- [Fact]
+ [Test]
public void InsertTestData()
{
var tree = new RBush();
@@ -70,7 +70,7 @@ public void InsertTestData()
tree.Envelope);
}
- [Fact]
+ [Test]
public void BulkLoadTestData()
{
var tree = new RBush();
@@ -81,7 +81,7 @@ public void BulkLoadTestData()
.SetEquals(tree.Search()));
}
- [Fact]
+ [Test]
public void BulkLoadSplitsTreeProperly()
{
var tree = new RBush(maxEntries: 4);
@@ -92,10 +92,10 @@ public void BulkLoadSplitsTreeProperly()
Assert.Equal(4, tree.Root.Height);
}
- [Fact]
+ [Test]
public void BulkLoadMergesTreesProperly()
{
- var smaller = RBushTests.GetPoints(10);
+ var smaller = GetPoints(10);
var tree1 = new RBush(maxEntries: 4);
tree1.BulkLoad(smaller);
tree1.BulkLoad(s_points);
@@ -112,16 +112,16 @@ public void BulkLoadMergesTreesProperly()
Assert.True(allPoints.SetEquals(tree2.Search()));
}
- [Fact]
+ [Test]
public void SearchReturnsEmptyResultIfNothingFound()
{
var tree = new RBush(maxEntries: 4);
tree.BulkLoad(s_points);
- Assert.Equal(Array.Empty(), tree.Search(new Envelope(200, 200, 210, 210)));
+ Assert.Equal([], tree.Search(new Envelope(200, 200, 210, 210)));
}
- [Fact]
+ [Test]
public void SearchReturnsMatchingResults()
{
var tree = new RBush(maxEntries: 4);
@@ -134,7 +134,7 @@ public void SearchReturnsMatchingResults()
Assert.True(shouldFindPoints.SetEquals(tree.Search(searchEnvelope)));
}
- [Fact]
+ [Test]
public void BasicRemoveTest()
{
var tree = new RBush(maxEntries: 4);
@@ -142,13 +142,13 @@ public void BasicRemoveTest()
var len = s_points.Length;
- tree.Delete(s_points[0]);
- tree.Delete(s_points[1]);
- tree.Delete(s_points[2]);
+ _ = tree.Delete(s_points[0]);
+ _ = tree.Delete(s_points[1]);
+ _ = tree.Delete(s_points[2]);
- tree.Delete(s_points[len - 1]);
- tree.Delete(s_points[len - 2]);
- tree.Delete(s_points[len - 3]);
+ _ = tree.Delete(s_points[len - 1]);
+ _ = tree.Delete(s_points[len - 2]);
+ _ = tree.Delete(s_points[len - 3]);
var shouldFindPoints = new HashSet(s_points
.Skip(3).Take(len - 6));
@@ -159,39 +159,39 @@ public void BasicRemoveTest()
tree.Envelope);
}
- [Fact]
+ [Test]
public void NonExistentItemCanBeDeleted()
{
var tree = new RBush(maxEntries: 4);
tree.BulkLoad(s_points);
- tree.Delete(new Point(13, 13, 13, 13));
+ _ = tree.Delete(new Point(13, 13, 13, 13));
Assert.Equal(s_points.Length, tree.Count);
}
- [Fact]
+ [Test]
public void DeleteTreeIsEmptyShouldNotThrow()
{
var tree = new RBush();
- tree.Delete(new Point(1, 1, 1, 1));
+ _ = tree.Delete(new Point(1, 1, 1, 1));
Assert.Equal(0, tree.Count);
}
- [Fact]
+ [Test]
public void DeleteDeletingLastPointShouldNotThrow()
{
var tree = new RBush();
var p = new Point(1, 1, 1, 1);
tree.Insert(p);
- tree.Delete(p);
+ _ = tree.Delete(p);
Assert.Equal(0, tree.Count);
}
- [Fact]
+ [Test]
public void ClearWorks()
{
var tree = new RBush(maxEntries: 4);
@@ -202,15 +202,15 @@ public void ClearWorks()
Assert.Empty(tree.Root.Children);
}
- [Fact]
+ [Test]
public void TestSearchAfterInsert()
{
var maxEntries = 9;
var tree = new RBush(maxEntries);
- var firstSet = s_points.Take(maxEntries);
- var firstSetEnvelope =
- firstSet.Aggregate(Envelope.EmptyBounds, (e, p) => e.Extend(p.Envelope));
+ var firstSet = s_points.Take(maxEntries).ToList();
+ var firstSetEnvelope = firstSet
+ .Aggregate(Envelope.EmptyBounds, (e, p) => e.Extend(p.Envelope));
foreach (var p in firstSet)
tree.Insert(p);
@@ -219,22 +219,22 @@ public void TestSearchAfterInsert()
.SetEquals(tree.Search(firstSetEnvelope)));
}
- [Fact]
+ [Test]
public void TestSearchAfterInsertWithSplitRoot()
{
var maxEntries = 4;
var tree = new RBush(maxEntries);
- var numFirstSet = maxEntries * maxEntries + 2; // Split-root will occur twice.
+ var numFirstSet = (maxEntries * maxEntries) + 2; // Split-root will occur twice.
var firstSet = s_points.Take(numFirstSet);
foreach (var p in firstSet)
tree.Insert(p);
var numExtraPoints = 5;
- var extraPointsSet = s_points.Skip(s_points.Length - numExtraPoints);
- var extraPointsSetEnvelope =
- extraPointsSet.Aggregate(Envelope.EmptyBounds, (e, p) => e.Extend(p.Envelope));
+ var extraPointsSet = s_points.Skip(s_points.Length - numExtraPoints).ToList();
+ var extraPointsSetEnvelope = extraPointsSet
+ .Aggregate(Envelope.EmptyBounds, (e, p) => e.Extend(p.Envelope));
foreach (var p in extraPointsSet)
tree.Insert(p);
@@ -245,21 +245,21 @@ public void TestSearchAfterInsertWithSplitRoot()
.SetEquals(tree.Search(extraPointsSetEnvelope)));
}
- [Fact]
+ [Test]
public void TestSearchAfterBulkLoadWithSplitRoot()
{
var maxEntries = 4;
var tree = new RBush(maxEntries);
- var numFirstSet = maxEntries * maxEntries + 2; // Split-root will occur twice.
+ var numFirstSet = (maxEntries * maxEntries) + 2; // Split-root will occur twice.
var firstSet = s_points.Take(numFirstSet);
tree.BulkLoad(firstSet);
var numExtraPoints = 5;
- var extraPointsSet = s_points.Skip(s_points.Length - numExtraPoints);
- var extraPointsSetEnvelope =
- extraPointsSet.Aggregate(Envelope.EmptyBounds, (e, p) => e.Extend(p.Envelope));
+ var extraPointsSet = s_points.Skip(s_points.Length - numExtraPoints).ToList();
+ var extraPointsSetEnvelope = extraPointsSet
+ .Aggregate(Envelope.EmptyBounds, (e, p) => e.Extend(p.Envelope));
tree.BulkLoad(extraPointsSet);
@@ -269,7 +269,7 @@ public void TestSearchAfterBulkLoadWithSplitRoot()
.SetEquals(tree.Search(extraPointsSetEnvelope)));
}
- [Fact]
+ [Test]
public void AdditionalRemoveTest()
{
var tree = new RBush();
@@ -279,24 +279,24 @@ public void AdditionalRemoveTest()
tree.Insert(p);
foreach (var p in s_points.Take(numDelete))
- tree.Delete(p);
+ _ = tree.Delete(p);
Assert.Equal(s_points.Length - numDelete, tree.Count);
Assert.True(new HashSet(s_points.Skip(numDelete))
.SetEquals(tree.Search()));
}
- [Fact]
+ [Test]
public void BulkLoadAfterDeleteTest1()
{
- var pts = RBushTests.GetPoints(20);
+ var pts = GetPoints(20);
var ptsDelete = pts.Take(18);
var tree = new RBush(maxEntries: 4);
tree.BulkLoad(pts);
foreach (var item in ptsDelete)
- tree.Delete(item);
+ _ = tree.Delete(item);
tree.BulkLoad(ptsDelete);
@@ -304,17 +304,17 @@ public void BulkLoadAfterDeleteTest1()
Assert.True(new HashSet(pts).SetEquals(tree.Search()));
}
- [Fact]
+ [Test]
public void BulkLoadAfterDeleteTest2()
{
- var pts = RBushTests.GetPoints(20);
+ var pts = GetPoints(20);
var ptsDelete = pts.Take(4);
var tree = new RBush(maxEntries: 4);
tree.BulkLoad(pts);
foreach (var item in ptsDelete)
- tree.Delete(item);
+ _ = tree.Delete(item);
tree.BulkLoad(ptsDelete);
@@ -323,18 +323,18 @@ public void BulkLoadAfterDeleteTest2()
.SetEquals(tree.Search()));
}
- [Fact]
+ [Test]
public void InsertAfterDeleteTest1()
{
- var pts = RBushTests.GetPoints(20);
- var ptsDelete = pts.Take(18);
+ var pts = GetPoints(20);
+ var ptsDelete = pts.Take(18).ToList();
var tree = new RBush(maxEntries: 4);
foreach (var item in pts)
tree.Insert(item);
foreach (var item in ptsDelete)
- tree.Delete(item);
+ _ = tree.Delete(item);
foreach (var item in ptsDelete)
tree.Insert(item);
@@ -344,18 +344,18 @@ public void InsertAfterDeleteTest1()
.SetEquals(tree.Search()));
}
- [Fact]
+ [Test]
public void InsertAfterDeleteTest2()
{
- var pts = RBushTests.GetPoints(20);
- var ptsDelete = pts.Take(4);
+ var pts = GetPoints(20);
+ var ptsDelete = pts.Take(4).ToList();
var tree = new RBush(maxEntries: 4);
foreach (var item in pts)
tree.Insert(item);
foreach (var item in ptsDelete)
- tree.Delete(item);
+ _ = tree.Delete(item);
foreach (var item in ptsDelete)
tree.Insert(item);
@@ -365,8 +365,8 @@ public void InsertAfterDeleteTest2()
.SetEquals(tree.Search()));
}
- private readonly List _missingEnvelopeTestData = new()
- {
+ private readonly List _missingEnvelopeTestData =
+ [
new Point(minX: 35.0457204123358, minY: 31.5946330633669, maxX: 35.1736414417038, maxY: 31.7658263429689),
new Point(minX: 35.0011136524732, minY: 31.6701999643473, maxX: 35.0119650302309, maxY: 31.6763344627552),
new Point(minX: 35.4519996266397, minY: 33.0521061332025, maxX: 35.6225745715679, maxY: 33.2873426178667),
@@ -461,9 +461,9 @@ public void InsertAfterDeleteTest2()
new Point(minX: 34.6095401534748, minY: 31.7162092103111, maxX: 35.0591449537042, maxY: 32.0383069680269),
new Point(minX: 34.7421834416939, minY: 32.003746842197, maxX: 35.0354837295605, maxY: 32.1466473164001),
new Point(minX: 34.7905250664059, minY: 32.1179577036489, maxX: 35.0581623045431, maxY: 32.3417009250043),
- };
+ ];
- [Fact]
+ [Test]
public void MissingEnvelopeTestInsertIndividually()
{
var tree = new RBush();
@@ -480,7 +480,7 @@ public void MissingEnvelopeTestInsertIndividually()
actual: tree.Search(envelope).Count);
}
- [Fact]
+ [Test]
public void TestBulk()
{
var tree = new RBush();
diff --git a/RBush.sln b/RBush.sln
index 832a1ea..026510d 100644
--- a/RBush.sln
+++ b/RBush.sln
@@ -5,12 +5,13 @@ VisualStudioVersion = 17.2.32616.157
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RBush", "RBush\RBush.csproj", "{24061D07-5EFA-4D72-9617-0C6671280FDF}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6EA781FE-7457-4266-93E4-6C2751DCB4CF}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".root", ".root", "{6EA781FE-7457-4266-93E4-6C2751DCB4CF}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.github\workflows\build.yml = .github\workflows\build.yml
.github\dependabot.yml = .github\dependabot.yml
Directory.Build.props = Directory.Build.props
+ Directory.Packages.props = Directory.Packages.props
README.md = README.md
EndProjectSection
EndProject
diff --git a/RBush/ArgumentNullException.cs b/RBush/ArgumentNullException.cs
new file mode 100644
index 0000000..60def7d
--- /dev/null
+++ b/RBush/ArgumentNullException.cs
@@ -0,0 +1,29 @@
+#if !NET6_0_OR_GREATER
+#pragma warning disable IDE0005 // Using directive is unnecessary.
+global using ArgumentNullException = RBush.ArgumentNullException;
+#pragma warning restore IDE0005 // Using directive is unnecessary.
+
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+
+namespace RBush;
+
+[Browsable(false)]
+[ExcludeFromCodeCoverage]
+internal static class ArgumentNullException
+{
+ /// Throws an if is null.
+ /// The reference type argument to validate as non-null.
+ /// The name of the parameter with which corresponds.
+ public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null)
+ {
+ if (argument is null)
+ Throw(paramName);
+ }
+
+ [DoesNotReturn]
+ private static void Throw(string? paramName) =>
+ throw new System.ArgumentNullException(paramName);
+}
+#endif
diff --git a/RBush/Envelope.cs b/RBush/Envelope.cs
index da9d2da..1bf0fe1 100644
--- a/RBush/Envelope.cs
+++ b/RBush/Envelope.cs
@@ -1,4 +1,4 @@
-using System.Runtime.InteropServices;
+using System.Runtime.InteropServices;
namespace RBush;
@@ -37,10 +37,10 @@ public readonly record struct Envelope(
/// Does not affect the current bounding box.
public Envelope Extend(in Envelope other) =>
new(
- MinX: Math.Min(this.MinX, other.MinX),
- MinY: Math.Min(this.MinY, other.MinY),
- MaxX: Math.Max(this.MaxX, other.MaxX),
- MaxY: Math.Max(this.MaxY, other.MaxY));
+ MinX: Math.Min(MinX, other.MinX),
+ MinY: Math.Min(MinY, other.MinY),
+ MaxX: Math.Max(MaxX, other.MaxX),
+ MaxY: Math.Max(MaxY, other.MaxY));
///
/// Intersects a bounding box to only include the common area
@@ -51,10 +51,10 @@ public Envelope Extend(in Envelope other) =>
/// Does not affect the current bounding box.
public Envelope Intersection(in Envelope other) =>
new(
- MinX: Math.Max(this.MinX, other.MinX),
- MinY: Math.Max(this.MinY, other.MinY),
- MaxX: Math.Min(this.MaxX, other.MaxX),
- MaxY: Math.Min(this.MaxY, other.MaxY));
+ MinX: Math.Max(MinX, other.MinX),
+ MinY: Math.Max(MinY, other.MinY),
+ MaxX: Math.Min(MaxX, other.MaxX),
+ MaxY: Math.Min(MaxY, other.MaxY));
///
/// Determines whether is contained
@@ -67,10 +67,10 @@ public Envelope Intersection(in Envelope other) =>
/// otherwise.
///
public bool Contains(in Envelope other) =>
- this.MinX <= other.MinX &&
- this.MinY <= other.MinY &&
- this.MaxX >= other.MaxX &&
- this.MaxY >= other.MaxY;
+ MinX <= other.MinX &&
+ MinY <= other.MinY &&
+ MaxX >= other.MaxX &&
+ MaxY >= other.MaxY;
///
/// Determines whether intersects
@@ -83,10 +83,10 @@ public bool Contains(in Envelope other) =>
/// otherwise.
///
public bool Intersects(in Envelope other) =>
- this.MinX <= other.MaxX &&
- this.MinY <= other.MaxY &&
- this.MaxX >= other.MinX &&
- this.MaxY >= other.MinY;
+ MinX <= other.MaxX &&
+ MinY <= other.MaxY &&
+ MaxX >= other.MinX &&
+ MaxY >= other.MinY;
///
/// A bounding box that contains the entire 2-d plane.
diff --git a/RBush/RBush.Node.cs b/RBush/RBush.Node.cs
index 49f09ea..32db79b 100644
--- a/RBush/RBush.Node.cs
+++ b/RBush/RBush.Node.cs
@@ -1,4 +1,4 @@
-namespace RBush;
+namespace RBush;
public partial class RBush
{
@@ -12,8 +12,8 @@ public class Node : ISpatialData
internal Node(List items, int height)
{
- this.Height = height;
- this.Items = items;
+ Height = height;
+ Items = items;
ResetEnvelope();
}
@@ -25,7 +25,7 @@ internal void Add(ISpatialData node)
internal void Remove(ISpatialData node)
{
- Items.Remove(node);
+ _ = Items.Remove(node);
ResetEnvelope();
}
diff --git a/RBush/RBush.Utilities.cs b/RBush/RBush.Utilities.cs
index 22cbf37..1a2594b 100644
--- a/RBush/RBush.Utilities.cs
+++ b/RBush/RBush.Utilities.cs
@@ -1,4 +1,4 @@
-namespace RBush;
+namespace RBush;
public partial class RBush
{
@@ -13,7 +13,7 @@ public partial class RBush
private List DoSearch(in Envelope boundingBox)
{
if (!Root.Envelope.Intersects(boundingBox))
- return new List();
+ return [];
var intersections = new List();
var queue = new Queue();
@@ -49,7 +49,7 @@ private List DoSearch(in Envelope boundingBox)
private List FindCoveringArea(in Envelope area, int depth)
{
var path = new List();
- var node = this.Root;
+ var node = Root;
while (true)
{
@@ -67,7 +67,9 @@ private List FindCoveringArea(in Envelope area, int depth)
if (newArea == nextArea
&& i.Envelope.Area >= next.Envelope.Area)
+ {
continue;
+ }
next = i;
nextArea = newArea;
@@ -81,7 +83,7 @@ private void Insert(ISpatialData data, int depth)
{
var path = FindCoveringArea(data.Envelope, depth);
- var insertNode = path[path.Count - 1];
+ var insertNode = path[^1];
insertNode.Add(data);
while (--depth >= 0)
@@ -95,13 +97,15 @@ private void Insert(ISpatialData data, int depth)
path[depth - 1].Add(newNode);
}
else
+ {
path[depth].ResetEnvelope();
+ }
}
}
#region SplitNode
private void SplitRoot(Node newNode) =>
- this.Root = new Node(new List { this.Root, newNode }, this.Root.Height + 1);
+ Root = new Node([Root, newNode], Root.Height + 1);
private Node SplitNode(Node node)
{
@@ -187,10 +191,9 @@ private Node BuildNodes(ArraySegment data, int height, int maxEntries)
return height == 1
? new Node(data.Cast().ToList(), height)
: new Node(
- new List
- {
+ [
BuildNodes(data, height - 1, _maxEntries),
- },
+ ],
height);
}
@@ -231,23 +234,21 @@ private static Envelope GetEnclosingEnvelope(IEnumerable items)
{
var envelope = Envelope.EmptyBounds;
foreach (var data in items)
- {
envelope = envelope.Extend(data.Envelope);
- }
+
return envelope;
}
- private List GetAllChildren(List list, Node n)
+ private static List GetAllChildren(List list, Node n)
{
if (n.IsLeaf)
{
- list.AddRange(
- n.Items.Cast());
+ list.AddRange(n.Items.Cast());
}
else
{
foreach (var node in n.Items.Cast())
- GetAllChildren(list, node);
+ _ = GetAllChildren(list, node);
}
return list;
diff --git a/RBush/RBush.cs b/RBush/RBush.cs
index 8267642..4ec601d 100644
--- a/RBush/RBush.cs
+++ b/RBush/RBush.cs
@@ -1,4 +1,4 @@
-using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.CodeAnalysis;
namespace RBush;
@@ -52,11 +52,11 @@ public RBush(int maxEntries)
///
public RBush(int maxEntries, IEqualityComparer comparer)
{
- this._comparer = comparer;
- this._maxEntries = Math.Max(MinimumMaxEntries, maxEntries);
- this._minEntries = Math.Max(MinimumMinEntries, (int)Math.Ceiling(this._maxEntries * DefaultFillFactor));
+ _comparer = comparer;
+ _maxEntries = Math.Max(MinimumMaxEntries, maxEntries);
+ _minEntries = Math.Max(MinimumMinEntries, (int)Math.Ceiling(_maxEntries * DefaultFillFactor));
- this.Clear();
+ Clear();
}
///
@@ -70,8 +70,8 @@ public RBush(int maxEntries, IEqualityComparer comparer)
[MemberNotNull(nameof(Root))]
public void Clear()
{
- this.Root = new Node(new List(), 1);
- this.Count = 0;
+ Root = new Node([], 1);
+ Count = 0;
}
///
@@ -81,7 +81,7 @@ public void Clear()
/// A list of every element contained in the .
///
public IReadOnlyList Search() =>
- GetAllChildren(new List(), this.Root);
+ GetAllChildren([], Root);
///
/// Get all of the elements from this
@@ -103,8 +103,8 @@ public IReadOnlyList Search(in Envelope boundingBox) =>
///
public void Insert(T item)
{
- Insert(item, this.Root.Height);
- this.Count++;
+ Insert(item, Root.Height);
+ Count++;
}
///
@@ -122,15 +122,15 @@ public void BulkLoad(IEnumerable items)
var data = items.ToArray();
if (data.Length == 0) return;
- if (this.Root.IsLeaf &&
- this.Root.Items.Count + data.Length < _maxEntries)
+ if (Root.IsLeaf &&
+ Root.Items.Count + data.Length < _maxEntries)
{
foreach (var i in data)
Insert(i);
return;
}
- if (data.Length < this._minEntries)
+ if (data.Length < _minEntries)
{
foreach (var i in data)
Insert(i);
@@ -138,32 +138,36 @@ public void BulkLoad(IEnumerable items)
}
var dataRoot = BuildTree(data);
- this.Count += data.Length;
+ Count += data.Length;
- if (this.Root.Items.Count == 0)
- this.Root = dataRoot;
- else if (this.Root.Height == dataRoot.Height)
+ if (Root.Items.Count == 0)
{
- if (this.Root.Items.Count + dataRoot.Items.Count <= this._maxEntries)
+ Root = dataRoot;
+ }
+ else if (Root.Height == dataRoot.Height)
+ {
+ if (Root.Items.Count + dataRoot.Items.Count <= _maxEntries)
{
foreach (var isd in dataRoot.Items)
- this.Root.Add(isd);
+ Root.Add(isd);
}
else
+ {
SplitRoot(dataRoot);
+ }
}
else
{
- if (this.Root.Height < dataRoot.Height)
+ if (Root.Height < dataRoot.Height)
{
#pragma warning disable IDE0180 // netstandard 1.2 doesn't support tuple
- var tmp = this.Root;
- this.Root = dataRoot;
+ var tmp = Root;
+ Root = dataRoot;
dataRoot = tmp;
#pragma warning restore IDE0180
}
- this.Insert(dataRoot, this.Root.Height - dataRoot.Height);
+ Insert(dataRoot, Root.Height - dataRoot.Height);
}
}
@@ -185,24 +189,24 @@ private bool DoDelete(Node node, T item)
if (node.IsLeaf)
{
var cnt = node.Items.RemoveAll(i => _comparer.Equals((T)i, item));
- if (cnt != 0)
- {
- Count -= cnt;
- node.ResetEnvelope();
- return true;
- }
- else
+ if (cnt == 0)
return false;
+
+ Count -= cnt;
+ node.ResetEnvelope();
+ return true;
+
}
var flag = false;
- foreach (Node n in node.Items)
+ foreach (var n in node.Items)
{
- flag |= DoDelete(n, item);
+ flag |= DoDelete((Node)n, item);
}
if (flag)
node.ResetEnvelope();
+
return flag;
}
}
diff --git a/RBush/RBush.csproj b/RBush/RBush.csproj
index 964aeae..d30ea6e 100644
--- a/RBush/RBush.csproj
+++ b/RBush/RBush.csproj
@@ -1,56 +1,47 @@
-
-
-
- netstandard1.2;netcoreapp3.1;net6.0
- RBush
- RBush
-
-
-
- RBush
- Spatial Index data structure; used to make it easier to find data points on a two dimensional plane.
-
- viceroypenguin
- .NET R-Tree Algorithm tree search spatial index
- Copyright © 2017-2022 Turning Code, LLC (and others)
-
- MIT
- readme.md
-
- true
- https://github.com/viceroypenguin/RBush
- git
-
- true
- snupkg
-
- true
-
-
-
-
-
-
-
-
- <_Parameter1>RBush.Test
-
-
-
-
-
-
-
-
-
-
- minor
- preview
- v
-
-
-
- System.Index;System.Range
-
+
+
+
+ net47;netstandard2.0;net8.0
+ true
+
+
+
+ RBush
+ Spatial Index data structure; used to make it easier to find data points on a two dimensional plane.
+
+ viceroypenguin
+ .NET R-Tree Algorithm tree search spatial index
+ Copyright © 2017-2024 Turning Code, LLC (and others)
+
+ MIT
+ readme.md
+
+ true
+ https://github.com/viceroypenguin/RBush
+ git
+
+ true
+
+
+
+
+
+
+
+
+ <_Parameter1>RBush.Test
+
+
+
+
+
+
+
+
+
+ minor
+ preview.0
+ v
+
diff --git a/RBush/RBush.Knn.cs b/RBush/RBushExtensions.cs
similarity index 98%
rename from RBush/RBush.Knn.cs
rename to RBush/RBushExtensions.cs
index a68744a..1e8f466 100644
--- a/RBush/RBush.Knn.cs
+++ b/RBush/RBushExtensions.cs
@@ -30,6 +30,8 @@ public static IReadOnlyList Knn(
Func? predicate = null)
where T : ISpatialData
{
+ ArgumentNullException.ThrowIfNull(tree);
+
var items = maxDistance == null
? tree.Search()
: tree.Search(