diff --git a/.editorconfig b/.editorconfig
index df5e28a..b981a9d 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,381 +1,80 @@
root=true
-# Microsoft .NET properties
-csharp_new_line_before_members_in_object_initializers=true
-
-##########################################
-# File Extension Settings
-##########################################
-
-# Visual Studio Solution Files
-[*.sln]
-indent_style=tab
-
-# Visual Studio XML Project Files
-[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
-indent_size=2
-
-# Various XML Configuration Files
-[*.{xml,config,props,targets,nuspec,resx,ruleset,vsixmanifest,vsct}]
-indent_size=2
-
-# JSON Files
-[*.{json,json5}]
-indent_size=2
-
-# YAML Files
-[*.{yml,yaml}]
-indent_size=2
-
-# Markdown Files
-[*.md]
-trim_trailing_whitespace=false
-
-# Web Files
-[*.{htm,html,js,ts,tsx,css,sass,scss,less,svg,vue}]
-indent_size=2
-
-# Batch Files
-[*.{cmd,bat}]
-end_of_line=crlf
-
-# Bash Files
-[*.sh]
-end_of_line=lf
-
-##########################################
-# .NET Language Conventions
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions
-##########################################
-
-# .NET Code Style Settings
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#net-code-style-settings
-[*.{cs,csx,cake,vb}]
-# "this." and "Me." qualifiers
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#this-and-me
-dotnet_style_qualification_for_field=true:silent
-dotnet_style_qualification_for_property=true:silent
-dotnet_style_qualification_for_method=true:silent
-dotnet_style_qualification_for_event=true:silent
-# Language keywords instead of framework type names for type references
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#language-keywords
-dotnet_style_predefined_type_for_locals_parameters_members=true:warning
-dotnet_style_predefined_type_for_member_access=true:warning
-# Modifier preferences
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#normalize-modifiers
-dotnet_style_require_accessibility_modifiers=always:warning
-csharp_preferred_modifier_order=public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
-visual_basic_preferred_modifier_order=Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async
-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
-# Expression-level preferences
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences
-dotnet_style_object_initializer=true:warning
-dotnet_style_collection_initializer=true:warning
-dotnet_style_explicit_tuple_names=true:warning
-dotnet_style_prefer_inferred_tuple_names=true:warning
-dotnet_style_prefer_inferred_anonymous_type_member_names=true:warning
-dotnet_style_prefer_auto_properties=true:warning
-dotnet_style_prefer_is_null_check_over_reference_equality_method=true:refactoring
-dotnet_style_prefer_conditional_expression_over_assignment=false:refactoring
-dotnet_style_prefer_conditional_expression_over_return=false:refactoring
-dotnet_style_prefer_compound_assignment=true:warning
-# Null-checking preferences
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#null-checking-preferences
-dotnet_style_coalesce_expression=true:warning
-dotnet_style_null_propagation=true:warning
-# Parameter preferences
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parameter-preferences
-dotnet_code_quality_unused_parameters=all:suggestion
-# More style options (Undocumented)
-# https://github.com/MicrosoftDocs/visualstudio-docs/issues/3641
-dotnet_style_operator_placement_when_wrapping=end_of_line
-
-# C# Code Style Settings
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-code-style-settings
-[*.{cs,csx,cake}]
-# Implicit and explicit types
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#implicit-and-explicit-types
-csharp_style_var_for_built_in_types=true:refactoring
-csharp_style_var_when_type_is_apparent=true:refactoring
-csharp_style_var_elsewhere=true:refactoring
-# Expression-bodied members
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-bodied-members
-csharp_style_expression_bodied_methods=true:refactoring
-csharp_style_expression_bodied_constructors=true:refactoring
-csharp_style_expression_bodied_operators=true:refactoring
-csharp_style_expression_bodied_properties=true:refactoring
-csharp_style_expression_bodied_indexers=true:refactoring
-csharp_style_expression_bodied_accessors=true:refactoring
-csharp_style_expression_bodied_lambdas=true:refactoring
-csharp_style_expression_bodied_local_functions=true:refactoring
-# Pattern matching
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#pattern-matching
-csharp_style_pattern_matching_over_is_with_cast_check=true:suggestion
-csharp_style_pattern_matching_over_as_with_null_check=true:suggestion
-# Inlined variable declarations
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#inlined-variable-declarations
-csharp_style_inlined_variable_declaration=true:warning
-# Expression-level preferences
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences
-csharp_prefer_simple_default_expression=true:warning
-# "Null" checking preferences
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-null-checking-preferences
-csharp_style_throw_expression=true:warning
-csharp_style_conditional_delegate_call=true:warning
-# Code block preferences
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#code-block-preferences
-csharp_prefer_braces=false
-# Unused value preferences
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#unused-value-preferences
-csharp_style_unused_value_expression_statement_preference=discard_variable:refactoring
-csharp_style_unused_value_assignment_preference=discard_variable:refactoring
-# Index and range preferences
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#index-and-range-preferences
-csharp_style_prefer_index_operator=true:warning
-csharp_style_prefer_range_operator=true:warning
-# Miscellaneous preferences
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#miscellaneous-preferences
-csharp_style_deconstructed_variable_declaration=true:warning
-csharp_style_pattern_local_over_anonymous_function=true:warning
-csharp_using_directive_placement=outside_namespace:silent
-csharp_prefer_static_local_function=true:warning
-csharp_prefer_simple_using_statement=false:warning
-
-##########################################
-# .NET Formatting Conventions
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-code-style-settings-reference#formatting-conventions
-##########################################
-
-# Organize usings
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#organize-using-directives
-dotnet_sort_system_directives_first=true
-# Newline options
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#new-line-options
-csharp_new_line_before_open_brace=all
-csharp_new_line_before_else=true
-csharp_new_line_before_catch=true
-csharp_new_line_before_finally=true
-csharp_new_line_before_members_in_object_initializers=true
-csharp_new_line_before_members_in_anonymous_types=true
-csharp_new_line_between_query_expression_clauses=true
-
-# Indentation options
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#indentation-options
-csharp_indent_case_contents=true
-csharp_indent_switch_labels=true
-csharp_indent_labels=no_change
-csharp_indent_block_contents=true
-csharp_indent_braces=false
-csharp_indent_case_contents_when_block=false
-# Spacing options
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#spacing-options
-csharp_space_after_cast=false
-csharp_space_after_keywords_in_control_flow_statements=true
-csharp_space_between_parentheses=false
-csharp_space_before_colon_in_inheritance_clause=true
-csharp_space_after_colon_in_inheritance_clause=true
-csharp_space_around_binary_operators=before_and_after
-csharp_space_between_method_declaration_parameter_list_parentheses=false
-csharp_space_between_method_declaration_empty_parameter_list_parentheses=false
-csharp_space_between_method_declaration_name_and_open_parenthesis=false
-csharp_space_between_method_call_parameter_list_parentheses=false
-csharp_space_between_method_call_empty_parameter_list_parentheses=false
-csharp_space_between_method_call_name_and_opening_parenthesis=false
-csharp_space_after_comma=true
-csharp_space_before_comma=false
-csharp_space_after_dot=false
-csharp_space_before_dot=false
-csharp_space_after_semicolon_in_for_statement=true
-csharp_space_before_semicolon_in_for_statement=false
-csharp_space_around_declaration_statements=false
-csharp_space_before_open_square_brackets=false
-csharp_space_between_empty_square_brackets=false
-csharp_space_between_square_brackets=false
-# Wrapping options
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#wrap-options
-csharp_preserve_single_line_statements=false
-csharp_preserve_single_line_blocks=false
-
-##########################################
-# .NET Naming Conventions
-# https://docs.microsoft.com/visualstudio/ide/editorconfig-naming-conventions
-##########################################
+[*]
+# Most of the standard properties are supported
+indent_size=4
+max_line_length=100
-[*.{cs,csx,cake,vb}]
+# Most frequently used .NET-coding-convention properties are supported
+csharp_space_between_parentheses=control_flow_statements,expressions,type_casts
+csharp_style_var_for_built_in_types=true:suggestion
-##########################################
-# Styles
-##########################################
+# dotnet_diagnostic rules are supported
+dotnet_diagnostic.cs1058.severity=suggestion
-# camel_case_style - Define the camelCase style
-dotnet_naming_style.camel_case_style.capitalization=camel_case
-# pascal_case_style - Define the PascalCase style
-dotnet_naming_style.pascal_case_style.capitalization=pascal_case
-# first_upper_style - The first character must start with an upper-case character
-dotnet_naming_style.first_upper_style.capitalization=first_word_upper
-# prefix_interface_with_i_style - Interfaces must be PascalCase and the first character of an interface must be an 'I'
-dotnet_naming_style.prefix_interface_with_i_style.capitalization=pascal_case
-dotnet_naming_style.prefix_interface_with_i_style.required_prefix=I
-# prefix_type_parameters_with_t_style - Generic Type Parameters must be PascalCase and the first character must be a 'T'
-dotnet_naming_style.prefix_type_parameters_with_t_style.capitalization=pascal_case
-dotnet_naming_style.prefix_type_parameters_with_t_style.required_prefix=T
-# disallowed_style - Anything that has this style applied is marked as disallowed
-# dotnet_naming_style.disallowed_style.capitalization=pascal_case
-# dotnet_naming_style.disallowed_style.required_prefix=____RULE_VIOLATION____
-# dotnet_naming_style.disallowed_style.required_suffix=____RULE_VIOLATION____
-# internal_error_style - This style should never occur... if it does, it's indicates a bug in file or in the parser using the file
-# dotnet_naming_style.internal_error_style.capitalization=pascal_case
-# dotnet_naming_style.internal_error_style.required_prefix=____INTERNAL_ERROR____
-# dotnet_naming_style.internal_error_style.required_suffix=____INTERNAL_ERROR____
+# JetBrains Rider custom properties for code formatting styles
+resharper_csharp_blank_lines_around_invocable=2
-##########################################
-# .NET Design Guideline Field Naming Rules
-# Naming rules for fields follow the .NET Framework design guidelines
-# https://docs.microsoft.com/dotnet/standard/design-guidelines/index
-##########################################
+# JetBrains Rider custom properties for code syntax styles
+csharp_default_private_modifier=explicit
+braces_for_ifelse=not_required
-# All public/protected/protected_internal constant fields must be PascalCase
-# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
-dotnet_naming_symbols.public_protected_constant_fields_group.applicable_accessibilities=public, protected, protected_internal
-dotnet_naming_symbols.public_protected_constant_fields_group.required_modifiers=const
-dotnet_naming_symbols.public_protected_constant_fields_group.applicable_kinds=field
-dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.symbols=public_protected_constant_fields_group
-dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.style=pascal_case_style
-dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.severity=warning
-
-# All public/protected/protected_internal static readonly fields must be PascalCase
-# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
-dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_accessibilities=public, protected, protected_internal
-dotnet_naming_symbols.public_protected_static_readonly_fields_group.required_modifiers=static, readonly
-dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_kinds=field
-dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.symbols=public_protected_static_readonly_fields_group
-dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.style=pascal_case_style
-dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.severity=warning
-
-# No other public/protected/protected_internal fields are allowed
-# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
-dotnet_naming_symbols.other_public_protected_fields_group.applicable_accessibilities=public, protected, protected_internal
-dotnet_naming_symbols.other_public_protected_fields_group.applicable_kinds=field
-dotnet_naming_rule.other_public_protected_fields_disallowed_rule.symbols=other_public_protected_fields_group
-dotnet_naming_rule.other_public_protected_fields_disallowed_rule.style=disallowed_style
-dotnet_naming_rule.other_public_protected_fields_disallowed_rule.severity=error
-
-##########################################
-# StyleCop Field Naming Rules
-# Naming rules for fields follow the StyleCop analyzers
-# This does not override any rules using disallowed_style above
-# https://github.com/DotNetAnalyzers/StyleCopAnalyzers
-##########################################
-
-# All constant fields must be PascalCase
-# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1303.md
-dotnet_naming_symbols.stylecop_constant_fields_group.applicable_accessibilities=public, internal, protected_internal, protected, private_protected, private
-dotnet_naming_symbols.stylecop_constant_fields_group.required_modifiers=const
-dotnet_naming_symbols.stylecop_constant_fields_group.applicable_kinds=field
-dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.symbols=stylecop_constant_fields_group
-dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.style=pascal_case_style
-dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.severity=warning
-
-# All static readonly fields must be PascalCase
-# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1311.md
-dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_accessibilities=public, internal, protected_internal, protected, private_protected, private
-dotnet_naming_symbols.stylecop_static_readonly_fields_group.required_modifiers=static, readonly
-dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_kinds=field
-dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.symbols=stylecop_static_readonly_fields_group
-dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.style=pascal_case_style
-dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.severity=warning
-
-# No non-private instance fields are allowed
-# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1401.md
-dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_accessibilities=public, internal, protected_internal, protected, private_protected
-dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_kinds=field
-dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.symbols=stylecop_fields_must_be_private_group
-dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.style=disallowed_style
-dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.severity=error
-
-# Private fields must be camelCase
-# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1306.md
-dotnet_naming_symbols.stylecop_private_fields_group.applicable_accessibilities=private
-dotnet_naming_symbols.stylecop_private_fields_group.applicable_kinds=field
-dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.symbols=stylecop_private_fields_group
-dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.style=camel_case_style
-dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.severity=warning
-
-# Local variables must be camelCase
-# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md
-dotnet_naming_symbols.stylecop_local_fields_group.applicable_accessibilities=local
-dotnet_naming_symbols.stylecop_local_fields_group.applicable_kinds=local
-dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.symbols=stylecop_local_fields_group
-dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.style=camel_case_style
-dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.severity=warning
-
-# This rule should never fire. However, it's included for at least two purposes:
-# First, it helps to understand, reason about, and root-case certain types of issues, such as bugs in .editorconfig parsers.
-# Second, it helps to raise immediate awareness if a new field type is added (as occurred recently in C#).
-dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_accessibilities=*
-dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_kinds=field
-dotnet_naming_rule.sanity_check_uncovered_field_case_rule.symbols=sanity_check_uncovered_field_case_group
-dotnet_naming_rule.sanity_check_uncovered_field_case_rule.style=internal_error_style
-dotnet_naming_rule.sanity_check_uncovered_field_case_rule.severity=error
-
-
-##########################################
-# Other Naming Rules
-##########################################
-
-# All of the following must be PascalCase:
-# - Namespaces
-# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-namespaces
-# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1300.md
-# - Classes and Enumerations
-# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
-# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1300.md
-# - Delegates
-# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces#names-of-common-types
-# - Constructors, Properties, Events, Methods
-# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-type-members
-dotnet_naming_symbols.element_group.applicable_kinds=namespace, class, enum, struct, delegate, event, method, property
-dotnet_naming_rule.element_rule.symbols=element_group
-dotnet_naming_rule.element_rule.style=pascal_case_style
-dotnet_naming_rule.element_rule.severity=warning
-
-# Interfaces use PascalCase and are prefixed with uppercase 'I'
-# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
-dotnet_naming_symbols.interface_group.applicable_kinds=interface
-dotnet_naming_rule.interface_rule.symbols=interface_group
-dotnet_naming_rule.interface_rule.style=prefix_interface_with_i_style
-dotnet_naming_rule.interface_rule.severity=warning
-
-# Generics Type Parameters use PascalCase and are prefixed with uppercase 'T'
-# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
-dotnet_naming_symbols.type_parameter_group.applicable_kinds=type_parameter
-dotnet_naming_rule.type_parameter_rule.symbols=type_parameter_group
-dotnet_naming_rule.type_parameter_rule.style=prefix_type_parameters_with_t_style
-dotnet_naming_rule.type_parameter_rule.severity=warning
-
-# Function parameters use camelCase
-# https://docs.microsoft.com/dotnet/standard/design-guidelines/naming-parameters
-dotnet_naming_symbols.parameters_group.applicable_kinds=parameter
-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
+# JetBrains Rider custom properties for code inspections
+resharper_possible_null_reference_exception_highlighting=error
+resharper_replace_with_string_is_null_or_empty_highlighting=none
# ReSharper properties
-resharper_braces_redundant=true
+resharper_accessor_owner_body=expression_body
+resharper_align_linq_query=true
+resharper_align_multiline_argument=true
+resharper_align_multiline_calls_chain=true
+resharper_align_multiline_expression=true
+resharper_align_multiline_extends_list=true
+resharper_align_multiline_for_stmt=true
+resharper_align_multiline_parameter=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_allow_comment_after_lbrace=true
+resharper_apply_auto_detected_rules=false
+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_csharp_blank_lines_around_region=2
+resharper_csharp_blank_lines_inside_region=2
+resharper_csharp_empty_block_style=together_same_line
+resharper_csharp_int_align_comments=true
+resharper_csharp_keep_blank_lines_in_code=1
+resharper_csharp_keep_blank_lines_in_declarations=1
+resharper_csharp_outdent_commas=true
+resharper_csharp_outdent_dots=true
+resharper_csharp_wrap_after_declaration_lpar=true
+resharper_csharp_wrap_after_invocation_lpar=true
+resharper_csharp_wrap_arguments_style=chop_if_long
+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_fixed_stmt=true
+resharper_indent_nested_foreach_stmt=true
+resharper_indent_nested_for_stmt=true
+resharper_indent_nested_lock_stmt=true
+resharper_indent_nested_usings_stmt=true
+resharper_indent_nested_while_stmt=true
+resharper_keep_existing_embedded_arrangement=false
+resharper_keep_existing_switch_expression_arrangement=false
resharper_local_function_body=expression_body
+resharper_max_array_initializer_elements_on_line=6
+resharper_max_enum_members_on_line=4
+resharper_max_formal_parameters_on_line=4
+resharper_max_initializer_elements_on_line=6
+resharper_max_invocation_arguments_on_line=6
+resharper_place_simple_embedded_statement_on_same_line=false
resharper_space_within_catch_parentheses=false
resharper_space_within_checked_parentheses=false
resharper_space_within_foreach_parentheses=false
@@ -385,38 +84,53 @@ resharper_space_within_parentheses=false
resharper_space_within_switch_parentheses=false
resharper_space_within_using_parentheses=false
resharper_space_within_while_parentheses=false
+resharper_wrap_array_initializer_style=chop_if_long
+resharper_wrap_before_linq_expression=false
+resharper_wrap_chained_method_calls=chop_if_long
+resharper_wrap_enum_declaration=chop_if_long
+resharper_wrap_linq_expressions=chop_if_long
+resharper_xmldoc_wrap_lines=false
-##########################################
-# License
-##########################################
-# The following applies as to the .editorconfig file ONLY, and is
-# included below for reference, per the requirements of the license
-# corresponding to this .editorconfig file.
-# See: https://github.com/RehanSaeed/EditorConfig
-#
-# MIT License
-#
-# Copyright (c) 2017-2019 Muhammad Rehan Saeed
-# Copyright (c) 2019 Henry Gabryjelski
-#
-# Permission is hereby granted, free of charge, to any
-# person obtaining a copy of this software and associated
-# documentation files (the "Software"), to deal in the
-# Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute,
-# sublicense, and/or sell copies of the Software, and to permit
-# persons to whom the Software is furnished to do so, subject
-# to the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-# OTHER DEALINGS IN THE SOFTWARE.
-##########################################
+# Microsoft .NET properties
+csharp_new_line_before_members_in_object_initializers=true
+csharp_new_line_between_query_expression_clauses=true
+csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
+csharp_style_expression_bodied_accessors=true:suggestion
+csharp_style_expression_bodied_constructors=true:none
+csharp_style_expression_bodied_methods=true:none
+csharp_style_expression_bodied_properties=true:suggestion
+csharp_style_var_elsewhere=true:suggestion
+csharp_style_var_when_type_is_apparent=true:suggestion
+dotnet_diagnostic.sa1101.severity=none
+dotnet_diagnostic.sa1124.severity=suggestion
+dotnet_diagnostic.sa1128.severity=none
+dotnet_diagnostic.sa1200.severity=none
+dotnet_diagnostic.sa1201.severity=none
+dotnet_diagnostic.sa1202.severity=none
+dotnet_diagnostic.sa1203.severity=none
+dotnet_diagnostic.sa1204.severity=none
+dotnet_diagnostic.sa1401.severity=none
+dotnet_diagnostic.sa1502.severity=none
+dotnet_diagnostic.sa1503.severity=none
+dotnet_diagnostic.sa1519.severity=none
+dotnet_diagnostic.sa1600.severity=none
+dotnet_diagnostic.sa1602.severity=none
+dotnet_diagnostic.sa1610.severity=none
+dotnet_diagnostic.sa1616.severity=none
+dotnet_diagnostic.sa1633.severity=none
+dotnet_diagnostic.sa1648.severity=none
+dotnet_style_parentheses_in_arithmetic_binary_operators=never_if_unnecessary:none
+dotnet_style_parentheses_in_other_binary_operators=never_if_unnecessary:none
+dotnet_style_parentheses_in_relational_binary_operators=never_if_unnecessary:none
+dotnet_style_predefined_type_for_locals_parameters_members=true:suggestion
+dotnet_style_predefined_type_for_member_access=true:suggestion
+dotnet_style_qualification_for_event=true:suggestion
+dotnet_style_qualification_for_field=true:suggestion
+dotnet_style_qualification_for_method=true:suggestion
+dotnet_style_qualification_for_property=true:suggestion
+dotnet_style_require_accessibility_modifiers=for_non_interface_members:suggestion
+
+[*.{appxmanifest,asax,ascx,aspx,build,cg,cginc,compute,cs,cshtml,dtd,fs,fsi,fsscript,fsx,hlsl,hlsli,hlslinc,master,ml,mli,nuspec,razor,resw,resx,shader,skin,usf,ush,vb,xaml,xamlx,xoml,xsd}]
+indent_style=space
+indent_size=4
+tab_width=4
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index bc73d5a..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-language: csharp
-mono: none
-sudo: required
-dist: xenial
-dotnet: 3.0
-
-script:
- - dotnet restore
- - dotnet build -c Release
- - dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:CoverletOutput=../coverage/opencover.xml
-
-after_success:
- - bash <(curl -s https://codecov.io/bash) -f "coverage/opencover.xml"
-
-branches:
- only:
- - master
- - develop
- - feature/*
-
-notifications:
- slack: paramdigma:FtUcdwSDOynOHw2M479kXHxs#geometry-library
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dee92d5..ae3db6b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## Unreleased
+## [0.1.0] - 21-Nov-2020
+
+So... another couple of release notes missing! 😅 What's important?
+
+- Added NURBS support in curves and surfaces.
+- Added some spatial search algorithms.
+- Improved testing and coverage.
## [0.0.6] - 14-June-2020
diff --git a/Paramdigma.Core.sln.DotSettings b/Paramdigma.Core.sln.DotSettings
index 0b65484..cfcafff 100644
--- a/Paramdigma.Core.sln.DotSettings
+++ b/Paramdigma.Core.sln.DotSettings
@@ -1,4 +1,12 @@

True
+ True
+ True
+ True
+
+
True
- True
\ No newline at end of file
+ True
+ True
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 30c1f5c..1c90d8d 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,6 @@
[![License](https://img.shields.io/github/license/Paramdigma/Core.svg)](https://github.com/Paramdigma/Core/blob/master/LICENSE)
![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/paramdigma/core?sort=semver)
-![GitHub commits since latest release (by SemVer)](https://img.shields.io/github/commits-since/paramdigma/core/latest/master?sort=semver)
![Main language](https://img.shields.io/github/languages/top/Paramdigma/Core.svg)
![Code Size](https://img.shields.io/github/languages/code-size/Paramdigma/Core.svg)
@@ -37,7 +36,7 @@ If you are looking for just a geometry library to plug into a project we also pr
## Documentation
-You can find the Docfx built documentation for the latest relase on the 'master' branch at:
+You can find the Docfx built documentation for the latest release on the 'master' branch at:
[https://paramdigma.com/Core/](https://paramdigma.com/Core/)
diff --git a/src/Collections/Interval.cs b/src/Collections/Interval.cs
index 4a7c82e..5a73b27 100644
--- a/src/Collections/Interval.cs
+++ b/src/Collections/Interval.cs
@@ -22,6 +22,7 @@ public Interval(double start, double end)
this.End = end;
}
+
///
/// Initializes a new instance of the struct from another interval.
///
@@ -30,6 +31,7 @@ public Interval(double start, double end)
public Interval(Interval interval)
: this(interval.Start, interval.End) { }
+
///
/// Gets a new unit interval.
///
@@ -40,21 +42,13 @@ public Interval(Interval interval)
/// Gets or sets the starting value of the interval.
///
/// Value will always be lower unless interval is inverted.
- public double Start
- {
- get;
- set;
- }
+ public double Start { get; set; }
///
/// Gets or sets the ending value of the interval.
///
/// Value will always be the biggest unless interval is inverted.
- public double End
- {
- get;
- set;
- }
+ public double End { get; set; }
///
/// Gets the space between the start and end of the interval.
@@ -66,6 +60,7 @@ public double End
///
public bool HasInvertedDirection => this.Length < 0;
+
///
/// Crop a number so that it's contained on the given interval.
///
@@ -81,6 +76,7 @@ public static double CropNumber(double number, Interval interval)
return number;
}
+
///
/// Remap a number from one interval to another.
///
@@ -93,9 +89,10 @@ public static double RemapNumber(double number, Interval fromInterval, Interval
var cropped = fromInterval.Contains(number) ? number : fromInterval.Crop(number);
var proportion = (cropped - fromInterval.Start) / Math.Abs(fromInterval.Length);
- return toInterval.Start + (toInterval.Length * proportion);
+ return toInterval.Start + toInterval.Length * proportion;
}
+
///
/// Crop a number so that it is contained inside this interval.
///
@@ -103,13 +100,16 @@ public static double RemapNumber(double number, Interval fromInterval, Interval
/// Cropped number.
public double Crop(double number) => CropNumber(number, this);
+
///
/// Remap a number from this interval to a given one.
///
/// Number to remap.
/// Interval to remap number to.
/// Remapped number inside given interval.
- public double Remap(double number, Interval toInterval) => RemapNumber(number, this, toInterval);
+ public double Remap(double number, Interval toInterval) =>
+ RemapNumber(number, this, toInterval);
+
///
/// Remap a number from this interval to a unit interval.
@@ -118,6 +118,7 @@ public static double RemapNumber(double number, Interval fromInterval, Interval
/// Value remaped from 0 to 1.
public double RemapToUnit(double number) => this.Remap(number, Unit);
+
///
/// Remap a number from a unit interval to this interval.
///
@@ -125,6 +126,7 @@ public static double RemapNumber(double number, Interval fromInterval, Interval
/// Remapped number.
public double RemapFromUnit(double number) => Unit.Remap(number, this);
+
///
/// Check if a number is contained inside this interval.
///
@@ -137,6 +139,7 @@ public bool Contains(double number)
return min <= number && number <= max;
}
+
///
/// Swap the Start and End values of this interval.
///
diff --git a/src/Collections/Matrix{T}.cs b/src/Collections/Matrix{T}.cs
index 3d489c8..e9570bb 100644
--- a/src/Collections/Matrix{T}.cs
+++ b/src/Collections/Matrix{T}.cs
@@ -14,12 +14,14 @@ public class Matrix
// https://codereview.stackexchange.com/questions/194732/class-matrix-implementation
private T[,] data;
+
///
/// Initializes a new instance of the class.
///
/// Size of the square Matrix.
public Matrix(int n) => this.data = new T[n, n];
+
///
/// Initializes a new instance of the class of the specified size.
///
@@ -27,23 +29,25 @@ public class Matrix
/// Row size.
public Matrix(int n, int m) => this.data = new T[n, m];
+
///
/// Initializes a new instance of the class from a 2D array.
///
/// 2D array of data.
public Matrix(T[,] data) => this.data = data;
+
///
/// Gets columns.
///
/// Number of columns on the Matrix.
- public int N => this.data.GetUpperBound(0) + 1;
+ public int N => this.data.GetLength(0);
///
/// Gets rows.
///
/// Number of rows on the Matrix.
- public int M => this.data.GetUpperBound(1) + 1;
+ public int M => this.data.GetLength(1);
///
/// Gets a specific item in the matrix.
@@ -52,6 +56,12 @@ public class Matrix
/// Column.
public ref T this[int row, int column] => ref this.data[row, column];
+ ///
+ /// Gets the amount of items in this matrix.
+ ///
+ public int Count => this.data.Length;
+
+
///
/// Get the row of a matrix at the specified index.
///
@@ -66,6 +76,7 @@ public T[] Row(int n)
return row;
}
+
///
/// Get the column of a matrix at the specified index.
///
@@ -80,34 +91,47 @@ public T[] Column(int m)
return col;
}
+
// ----- ORDERING METHODS -----
+
///
/// Turns columns into rows and rows into columns.
///
public void FlipMatrix() => throw
- // TODO: Implement FlipMatrix()
- new NotImplementedException();
+ // TODO: Implement FlipMatrix()
+ new NotImplementedException();
+
///
/// Increment Matrix column size by a specified amount.
/// It accepts both increasing and decreasing the size.
///
/// Positive or negative increment.
- public void IncrementColumns(int incrementN) => this.ResizeMatrix(ref this.data, this.N + incrementN, this.M);
+ public void IncrementColumns(int incrementN) => this.ResizeMatrix(
+ ref this.data,
+ this.N + incrementN,
+ this.M);
+
///
/// Increment Matrix row size by a specified amount.
/// It accepts both increasing and decreasing the size.
///
/// Positive or negative increment.
- public void IncrementRows(int incrementM) => this.ResizeMatrix(ref this.data, this.N, this.M + incrementM);
+ public void IncrementRows(int incrementM) => this.ResizeMatrix(
+ ref this.data,
+ this.N,
+ this.M + incrementM);
+
///
/// Increase or decrease the matrix size symetrically.
///
/// Symetric increase/decrease.
- public void IncrementMatrixSize(int symetricIncrement) => this.IncrementMatrixSize(symetricIncrement, symetricIncrement);
+ public void IncrementMatrixSize(int symetricIncrement) =>
+ this.IncrementMatrixSize(symetricIncrement, symetricIncrement);
+
///
/// Increase or decrease the column size of the matrix.
@@ -120,6 +144,7 @@ public void IncrementMatrixSize(int columnIncrement, int rowIncrement)
this.IncrementRows(rowIncrement);
}
+
///
/// Obtains all neighbour entities surrounding the specified matrix coordinates.
///
@@ -135,6 +160,7 @@ public List GetAllNeighboursAt(int column, int row)
return neighbours;
}
+
///
/// Obtains corner neighbour entities surrounding the specified matrix coordinates.
///
@@ -145,6 +171,7 @@ public List GetAllNeighboursAt(int column, int row)
// TODO: Implement GetCornerNeighboursOfEntityAt()
new NotImplementedException();
+
///
/// Obtains contiguous neighbour entities surrounding the specified matrix coordinates.
///
@@ -155,10 +182,12 @@ public List GetAllNeighboursAt(int column, int row)
// TODO: Implement GetContiguousNeighboursOfEntityAt()
new NotImplementedException();
+
///
/// Resizes any given 2 dimensional array.
/// It accepts smaller and bigger array outputs.
- /// Obtained from: https://stackoverflow.com/questions/6539571/how-to-resize-multidimensional-2d-array-in-c .
+ /// Obtained from:
+ /// https://stackoverflow.com/questions/6539571/how-to-resize-multidimensional-2d-array-in-c .
///
/// 2D Array to resize.
/// Number of resulting columns in the array.
diff --git a/src/Curves/Geodesics.cs b/src/Curves/Geodesics.cs
index 4923f4a..4205bc2 100644
--- a/src/Curves/Geodesics.cs
+++ b/src/Curves/Geodesics.cs
@@ -19,7 +19,12 @@ public static class Geodesics
/// Maximum iterations.
/// Geodesic curves.
/// True if successful.
- public static bool StartDir(MeshPoint meshPoint, Vector3d vector, Mesh mesh, int maxIter, out List geodesic)
+ public static bool StartDir(
+ MeshPoint meshPoint,
+ Vector3d vector,
+ Mesh mesh,
+ int maxIter,
+ out List geodesic)
{
// Get initial face on the mesh
var initialFace = mesh.Faces[meshPoint.FaceIndex];
@@ -48,8 +53,12 @@ public static bool StartDir(MeshPoint meshPoint, Vector3d vector, Mesh mesh, int
var nextFace = halfEdge.Twin.Face;
// Flip vector to next face
- var perpVector = Vector3d.CrossProduct(thisDirection, MeshGeometry.FaceNormal(thisFace));
- var nextVector = Vector3d.CrossProduct(MeshGeometry.FaceNormal(nextFace), perpVector);
+ var perpVector = Vector3d.CrossProduct(
+ thisDirection,
+ MeshGeometry.FaceNormal(thisFace));
+ var nextVector = Vector3d.CrossProduct(
+ MeshGeometry.FaceNormal(nextFace),
+ perpVector);
// Assign iteration variables to current
thisPoint = nextPoint;
diff --git a/src/Curves/LevelSets.cs b/src/Curves/LevelSets.cs
index 0b7b2a1..1f1c2f4 100644
--- a/src/Curves/LevelSets.cs
+++ b/src/Curves/LevelSets.cs
@@ -16,14 +16,17 @@ public static class LevelSets
/// List of level values to be computed.
/// The mesh to compute the level-sets in.
/// Resulting level sets.
- public static void ComputeLevels(string valueKey, List levels, Mesh mesh, out List> levelSets)
+ public static void ComputeLevels(
+ string valueKey,
+ List levels,
+ Mesh mesh,
+ out List> levelSets)
{
var resultLines = new List>();
for (var i = 0; i < levels.Count; i++)
resultLines.Add(new List());
- var iter = 0;
foreach (var face in mesh.Faces)
{
var count = 0;
@@ -34,13 +37,12 @@ public static void ComputeLevels(string valueKey, List levels, Mesh mesh
count++;
}
-
- iter++;
}
levelSets = resultLines;
}
+
///
/// Compute the level on a specified face.
///
@@ -52,16 +54,23 @@ public static void ComputeLevels(string valueKey, List levels, Mesh mesh
public static bool GetFaceLevel(string valueKey, double level, MeshFace face, out Line line)
{
var adj = face.AdjacentVertices();
- var vertexValues = new List {adj[0].UserValues[valueKey], adj[1].UserValues[valueKey], adj[2].UserValues[valueKey]};
+ var vertexValues = new List
+ {
+ adj[0].UserValues[valueKey],
+ adj[1].UserValues[valueKey],
+ adj[2].UserValues[valueKey]
+ };
var above = new List();
var below = new List();
for (var i = 0; i < vertexValues.Count; i++)
+ {
if (vertexValues[i] < level)
below.Add(i);
else
above.Add(i);
+ }
if (above.Count == 3 || below.Count == 3)
{
@@ -74,20 +83,23 @@ public static bool GetFaceLevel(string valueKey, double level, MeshFace face, ou
var intersectionPoints = new List();
foreach (var i in above)
- foreach (var j in below)
{
- var diff = vertexValues[i] - vertexValues[j];
- var desiredDiff = level - vertexValues[j];
- var unitizedDistance = desiredDiff / diff;
- var edgeV = adj[i] - adj[j];
- var levelPoint = adj[j] + (edgeV * unitizedDistance);
- intersectionPoints.Add(levelPoint);
+ foreach (var j in below)
+ {
+ var diff = vertexValues[i] - vertexValues[j];
+ var desiredDiff = level - vertexValues[j];
+ var unitizedDistance = desiredDiff / diff;
+ var edgeV = adj[i] - adj[j];
+ var levelPoint = adj[j] + edgeV * unitizedDistance;
+ intersectionPoints.Add(levelPoint);
+ }
}
line = new Line(intersectionPoints[0], intersectionPoints[1]);
return true;
}
+
///
/// Compute the gradient on a given mesh given some per-vertex values.
///
@@ -103,6 +115,7 @@ public static List ComputeGradientField(string valueKey, Mesh mesh)
return gradientField;
}
+
///
/// Compute the gradient on a given mesh face given some per-vertex values.
///
@@ -121,7 +134,7 @@ public static Vector3d ComputeFaceGradient(string valueKey, MeshFace face)
var gk = adjacentVertices[2].UserValues[valueKey];
var faceNormal = face.Normal / (2 * face.Area);
- var rotatedGradient = ((gi * (k - j)) + (gj * (i - k)) + (gk * (j - i))) / (2 * face.Area);
+ var rotatedGradient = (gi * (k - j) + gj * (i - k) + gk * (j - i)) / (2 * face.Area);
var gradient = rotatedGradient.Cross(faceNormal);
return gradient;
diff --git a/src/Exceptions/UnsetGeometryException.cs b/src/Exceptions/UnsetGeometryException.cs
index 52b5ddb..3726a77 100644
--- a/src/Exceptions/UnsetGeometryException.cs
+++ b/src/Exceptions/UnsetGeometryException.cs
@@ -10,10 +10,14 @@ public class UnsetGeometryException : Exception
///
public UnsetGeometryException() { }
+
///
public UnsetGeometryException(string message) : base(message) { }
+
///
- public UnsetGeometryException(string message, Exception innerException) : base(message, innerException) { }
+ public UnsetGeometryException(string message, Exception innerException) : base(
+ message,
+ innerException) { }
}
}
\ No newline at end of file
diff --git a/src/Extensions/Lists.cs b/src/Extensions/Lists.cs
index 262f83b..fba4b2f 100644
--- a/src/Extensions/Lists.cs
+++ b/src/Extensions/Lists.cs
@@ -16,6 +16,7 @@ public static class Lists
/// //TODO.
public static List RepeatedDefault(int count) => Repeated(default(T), count);
+
///
/// Initializes a new list full of objects initialized the values of the specified instance of T.
///
diff --git a/src/Geometry/2D/BoundingBox2d.cs b/src/Geometry/2D/BoundingBox2d.cs
index b563386..a6c7a8e 100644
--- a/src/Geometry/2D/BoundingBox2d.cs
+++ b/src/Geometry/2D/BoundingBox2d.cs
@@ -9,6 +9,7 @@ public class BoundingBox2d
{
///
/// Initializes a new instance of the class from 2 points.
+ /// Coordinates will automatically be corrected if the corners are not consistent with naming (i.e. bottomLeftCorner is actually topLeft..)
///
/// Bottom left corner.
/// Top right corner.
@@ -22,6 +23,7 @@ public BoundingBox2d(Point2d bottomLeftCorner, Point2d topRightCorner)
this.YDomain.FlipDirection();
}
+
///
/// Initializes a new instance of the class from a polyline.
///
@@ -33,42 +35,36 @@ public BoundingBox2d(Polyline2d polyline)
var xMax = polyline.Vertices[0].X;
var yMax = polyline.Vertices[0].Y;
- polyline.Vertices.ForEach(vertex =>
- {
- if (vertex.X < xMin)
- xMin = vertex.X;
- if (vertex.X > xMax)
- xMax = vertex.X;
+ polyline.Vertices.ForEach(
+ vertex =>
+ {
+ if (vertex.X < xMin)
+ xMin = vertex.X;
+ if (vertex.X > xMax)
+ xMax = vertex.X;
- if (vertex.Y < yMin)
- yMin = vertex.Y;
- if (vertex.X > yMax)
- yMax = vertex.Y;
- });
+ if (vertex.Y < yMin)
+ yMin = vertex.Y;
+ if (vertex.X > yMax)
+ yMax = vertex.Y;
+ });
this.XDomain = new Interval(xMin, xMax);
this.YDomain = new Interval(yMin, yMax);
}
+
///
/// Gets or sets the Domain in the X direction.
///
///
- public Interval XDomain
- {
- get;
- set;
- }
+ public Interval XDomain { get; set; }
///
/// Gets or sets the Domain in the Y direction.
///
///
- public Interval YDomain
- {
- get;
- set;
- }
+ public Interval YDomain { get; set; }
///
/// Gets the Bottom left corner of the BBox.
@@ -94,28 +90,60 @@ public Interval YDomain
///
public Point2d TopRight => new Point2d(this.XDomain.End, this.YDomain.End);
+ ///
+ /// Gets the midpoint at the left edge of the box.
+ ///
public Point2d MidLeft => new Point2d(this.XDomain.Start, this.YDomain.RemapFromUnit(0.5));
+ ///
+ /// Gets the midpoint at the right edge of the box.
+ ///
public Point2d MidRight => new Point2d(this.XDomain.End, this.YDomain.RemapFromUnit(0.5));
- public Point2d MidBottom => new Point2d(this.XDomain.RemapFromUnit(0.5), this.YDomain.Start);
+ ///
+ /// Gets the midpoint at the bottom edge of the box.
+ ///
+ public Point2d MidBottom =>
+ new Point2d(this.XDomain.RemapFromUnit(0.5), this.YDomain.Start);
+ ///
+ /// Gets the midpoint at the top edge of the box.
+ ///
public Point2d MidTop => new Point2d(this.XDomain.RemapFromUnit(0.5), this.YDomain.End);
///
/// Gets the center of the bounding BBox.
///
- public Point2d Center => new Point2d(this.XDomain.RemapFromUnit(0.5), this.YDomain.RemapFromUnit(0.5));
+ public Point2d Center =>
+ new Point2d(this.XDomain.RemapFromUnit(0.5), this.YDomain.RemapFromUnit(0.5));
+
+
+ ///
+ /// Checks if a point is contained inside the box.
+ ///
+ /// Point to test containment.
+ /// True if point is contained inside the bounding box.
+ public bool ContainsPoint(Point2d pt) =>
+ this.XDomain.Contains(pt.X) && this.YDomain.Contains(pt.Y);
- public bool ContainsPoint(Point2d pt) => this.XDomain.Contains(pt.X) && this.YDomain.Contains(pt.Y);
+ ///
+ /// Checks if a box intersects with this instance.
+ ///
+ /// Box to check intersection against.
+ /// True if intersection exists.
public bool IntersectsBox(BoundingBox2d box)
{
- var xCheck = this.XDomain.Contains(box.XDomain.Start) || this.XDomain.Contains(box.XDomain.End);
- var yCheck = this.YDomain.Contains(box.YDomain.Start) || this.YDomain.Contains(box.YDomain.End);
+ var xCheck = this.XDomain.Contains(box.XDomain.Start)
+ || this.XDomain.Contains(box.XDomain.End);
+ var yCheck = this.YDomain.Contains(box.YDomain.Start)
+ || this.YDomain.Contains(box.YDomain.End);
return xCheck && yCheck;
}
- public override string ToString() => $"BBox2d [{this.XDomain.Start};{this.YDomain.Start}]-[{this.XDomain.End};{this.YDomain.End}]";
+
+ ///
+ public override string ToString() =>
+ $"BBox2d [{this.XDomain.Start};{this.YDomain.Start}]-[{this.XDomain.End};{this.YDomain.End}]";
}
}
\ No newline at end of file
diff --git a/src/Geometry/2D/Delaunay.cs b/src/Geometry/2D/Delaunay.cs
index 4d5fab3..a49a264 100644
--- a/src/Geometry/2D/Delaunay.cs
+++ b/src/Geometry/2D/Delaunay.cs
@@ -1,71 +1,76 @@
using System.Collections.Generic;
+using System.Linq;
namespace Paramdigma.Core.Geometry
{
///
- /// Class holding all the delaunay and vornoi classes in 2 dimensions.
+ /// Class holding all the delaunay and Voronoi classes in 2 dimensions.
///
public static class Delaunay
{
- public static List Compute(List points, List border)
+ ///
+ /// Compute the delaunay triangulation of a given list of points.
+ ///
+ /// Points to find delaunay tessellation.
+ /// Border to start from.
+ /// List of .
+ public static IEnumerable Compute(
+ IEnumerable points,
+ IEnumerable border)
{
var triangulation = new List(border);
- points.Reverse();
foreach (var point in points)
{
- var badTriangles = FindBadTriangles(point, triangulation);
+ var badTriangles = FindBadTriangles(point, triangulation).ToList();
var polygon = FindHoleBoundaries(badTriangles);
foreach (var triangle in badTriangles)
{
foreach (var vertex in triangle.Vertices)
vertex.AdjacentTriangles.Remove(triangle);
- if (triangulation.Contains(triangle)) triangulation.Remove(triangle);
- }
- foreach (var edge in polygon)
- {
- var triangle = new DelaunayTriangle(point, edge.StartPoint, edge.EndPoint);
- triangulation.Add(triangle);
+ if (triangulation.Contains(triangle))
+ triangulation.Remove(triangle);
}
+
+ triangulation.AddRange(
+ polygon.Select(
+ edge => new DelaunayTriangle(point, edge.StartPoint, edge.EndPoint)));
}
return triangulation;
}
- public static List Voronoi(List triangulation)
- {
- var voronoiEdges = new List();
- foreach (var triangle in triangulation)
- foreach (var neigbour in triangle.TrianglesWithSharedEdges())
- {
- var edge = new DelaunayEdge(triangle.Circumcenter, neigbour.Circumcenter);
- voronoiEdges.Add(edge);
- }
- return voronoiEdges;
- }
+ ///
+ /// Computes the Voronoi diagram of a given Delaunay triangulation as a list of
+ /// instances.
+ ///
+ /// Delaunay triangulation.
+ /// Collection of lines representing the Voronoi cells.
+ public static IEnumerable Voronoi(IEnumerable triangulation)
+ =>
+ from triangle in triangulation
+ from neighbour in triangle.TrianglesWithSharedEdges()
+ select new Line2d(triangle.Circumcenter, neighbour.Circumcenter);
- private static List FindHoleBoundaries(List badTriangles)
+
+ private static IEnumerable FindHoleBoundaries(
+ IEnumerable badTriangles)
{
var boundaryEdges = new List();
var duplicateEdges = new List();
foreach (var triangle in badTriangles)
{
- var e = new DelaunayEdge(triangle.Vertices[0], triangle.Vertices[1]);
- if (!boundaryEdges.Contains(e))
- boundaryEdges.Add(e);
- else
- duplicateEdges.Add(e);
- var f = new DelaunayEdge(triangle.Vertices[1], triangle.Vertices[2]);
- if (!boundaryEdges.Contains(f))
- boundaryEdges.Add(f);
- else
- duplicateEdges.Add(f);
- var j = new DelaunayEdge(triangle.Vertices[2], triangle.Vertices[0]);
- if (!boundaryEdges.Contains(j))
- boundaryEdges.Add(j);
- else
- duplicateEdges.Add(j);
+ for (var i = 0; i < triangle.Vertices.Count; i++)
+ {
+ var e = new DelaunayEdge(
+ triangle.Vertices[i],
+ triangle.Vertices[(i + 1) % triangle.Vertices.Count]);
+ if (!boundaryEdges.Contains(e))
+ boundaryEdges.Add(e);
+ else
+ duplicateEdges.Add(e);
+ }
}
for (var i = boundaryEdges.Count - 1; i >= 0; i--)
@@ -78,13 +83,10 @@ private static List FindHoleBoundaries(List badT
return boundaryEdges;
}
- private static List FindBadTriangles(DelaunayPoint point, List triangles)
- {
- var badTriangles = new List();
- foreach (var triangle in triangles)
- if (triangle.IsPointInsideCircumcircle(point))
- badTriangles.Add(triangle);
- return badTriangles;
- }
+
+ private static IEnumerable FindBadTriangles(
+ Point2d point,
+ IEnumerable triangles)
+ => triangles.Where(triangle => triangle.IsPointInsideCircumcircle(point));
}
}
\ No newline at end of file
diff --git a/src/Geometry/2D/DelaunayEdge.cs b/src/Geometry/2D/DelaunayEdge.cs
index 6989ac2..ee5c493 100644
--- a/src/Geometry/2D/DelaunayEdge.cs
+++ b/src/Geometry/2D/DelaunayEdge.cs
@@ -1,29 +1,52 @@
namespace Paramdigma.Core.Geometry
{
+ ///
+ /// Represents a connection between two points in a Delaunay triangulation.
+ ///
public class DelaunayEdge
{
+ ///
+ /// The edge's end point.
+ ///
public DelaunayPoint EndPoint;
+
+ ///
+ /// The edge's start point.
+ ///
public DelaunayPoint StartPoint;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Start point.
+ /// End point.
public DelaunayEdge(DelaunayPoint startPoint, DelaunayPoint endPoint)
{
this.StartPoint = startPoint;
this.EndPoint = endPoint;
}
+
+ ///
public override bool Equals(object obj)
{
- if (obj == null) return false;
- if (obj.GetType() != this.GetType()) return false;
- var edge = obj as DelaunayEdge;
+ if (!(obj is DelaunayEdge edge))
+ return false;
var samePoints = this.StartPoint == edge.StartPoint && this.EndPoint == edge.EndPoint;
- var samePointsReversed = this.StartPoint == edge.EndPoint && this.EndPoint == edge.StartPoint;
+ var samePointsReversed =
+ this.StartPoint == edge.EndPoint && this.EndPoint == edge.StartPoint;
return samePoints || samePointsReversed;
}
+
+ ///
public override int GetHashCode()
{
- var hCode = (int)this.StartPoint.X ^ (int)this.StartPoint.Y ^ (int)this.EndPoint.X ^ (int)this.EndPoint.Y;
+ var hCode = ( int ) this.StartPoint.X
+ ^ ( int ) this.StartPoint.Y
+ ^ ( int ) this.EndPoint.X
+ ^ ( int ) this.EndPoint.Y;
return hCode.GetHashCode();
}
}
diff --git a/src/Geometry/2D/DelaunayPoint.cs b/src/Geometry/2D/DelaunayPoint.cs
index f35b4b1..f95b367 100644
--- a/src/Geometry/2D/DelaunayPoint.cs
+++ b/src/Geometry/2D/DelaunayPoint.cs
@@ -2,10 +2,31 @@
namespace Paramdigma.Core.Geometry
{
+ ///
+ /// Represents a point in a delaunay triangulation with adjacency information.
+ ///
public class DelaunayPoint : Point2d
{
+ ///
+ /// List of adjacent triangles of this point.
+ ///
public List AdjacentTriangles;
- public DelaunayPoint(double x, double y) : base(x, y) => this.AdjacentTriangles = new List();
+
+ ///
+ /// Initializes a new instance of the class from it's coordinates.
+ ///
+ /// X Coordinate.
+ /// Y Coordinate.
+ public DelaunayPoint(double x, double y) : base(x, y) =>
+ this.AdjacentTriangles = new List();
+
+
+ ///
+ /// Initializes a new instance of the class from a
+ /// instance.
+ ///
+ /// Point to create from.
+ public DelaunayPoint(Point2d point) : this(point.X, point.Y) { }
}
}
\ No newline at end of file
diff --git a/src/Geometry/2D/DelaunayTriangle.cs b/src/Geometry/2D/DelaunayTriangle.cs
index 5d7965e..9c0d38d 100644
--- a/src/Geometry/2D/DelaunayTriangle.cs
+++ b/src/Geometry/2D/DelaunayTriangle.cs
@@ -1,19 +1,40 @@
using System;
using System.Collections.Generic;
+using System.Linq;
namespace Paramdigma.Core.Geometry
{
+ ///
+ /// Represents a triangle in a delaunay triangulation operation.
+ ///
public class DelaunayTriangle
{
- public DelaunayPoint Circumcenter;
+ ///
+ /// Circumcenter of this triangle.
+ ///
+ public Point2d Circumcenter;
+
+ ///
+ /// Squared radius of the triangle's circumcircle.
+ ///
public double RadiusSquared;
- public List Vertices = new List();
+ ///
+ /// List of vertices of this triangle.
+ ///
+ public List Vertices;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Point A.
+ /// Point B.
+ /// Point C.
public DelaunayTriangle(DelaunayPoint point1, DelaunayPoint point2, DelaunayPoint point3)
{
this.Vertices = new List();
- if (!this.IsCounterClockwise(point1, point2, point3))
+ if (!IsCounterClockwise(point1, point2, point3))
{
this.Vertices.Add(point1);
this.Vertices.Add(point3);
@@ -32,61 +53,85 @@ public DelaunayTriangle(DelaunayPoint point1, DelaunayPoint point2, DelaunayPoin
this.UpdateCircumcircle();
}
- public List TrianglesWithSharedEdges()
+
+ ///
+ /// Returns a collection of neighbouring triangles.
+ ///
+ /// Collection of triangles.
+ public IEnumerable TrianglesWithSharedEdges()
{
var neighbours = new List();
- foreach (var vertex in this.Vertices)
- foreach (var sharedTriangle in vertex.AdjacentTriangles)
- if (this.SharesEdgeWidth(sharedTriangle) && neighbours.Contains(sharedTriangle) == false && sharedTriangle != this)
- neighbours.Add(sharedTriangle);
+ foreach (var sharedTriangle in
+ from vertex in this.Vertices
+ from sharedTriangle in vertex.AdjacentTriangles
+ where this.SharesEdgeWidth(sharedTriangle)
+ && neighbours.Contains(sharedTriangle) == false
+ && sharedTriangle != this
+ select sharedTriangle)
+ neighbours.Add(sharedTriangle);
+
return neighbours;
}
- public void UpdateCircumcircle()
+
+ ///
+ /// Checks if a point is inside this triangle's circumcircle.
+ ///
+ /// Point2d to check.
+ /// True if inside.
+ public bool IsPointInsideCircumcircle(Point2d point)
+ {
+ var dSquared = (point.X - this.Circumcenter.X) * (point.X - this.Circumcenter.X)
+ + (point.Y - this.Circumcenter.Y) * (point.Y - this.Circumcenter.Y);
+ return dSquared < this.RadiusSquared;
+ }
+
+
+ private static bool IsCounterClockwise(Point2d point1, Point2d point2, Point2d point3)
+ {
+ var result = (point2.X - point1.X) * (point3.Y - point1.Y)
+ - (point3.X - point1.X) * (point2.Y - point1.Y);
+ return result > 0;
+ }
+
+
+ private void UpdateCircumcircle()
{
var p0 = this.Vertices[0];
var p1 = this.Vertices[1];
var p2 = this.Vertices[2];
- var dA = (p0.X * p0.X) + (p0.Y * p0.Y);
- var dB = (p1.X * p1.X) + (p1.Y * p1.Y);
- var dC = (p2.X * p2.X) + (p2.Y * p2.Y);
+ var dA = p0.X * p0.X + p0.Y * p0.Y;
+ var dB = p1.X * p1.X + p1.Y * p1.Y;
+ var dC = p2.X * p2.X + p2.Y * p2.Y;
- var aux1 = (dA * (p2.Y - p1.Y)) + (dB * (p0.Y - p2.Y)) + (dC * (p1.Y - p0.Y));
- var aux2 = -((dA * (p2.X - p1.X)) + (dB * (p0.X - p2.X)) + (dC * (p1.X - p0.X)));
- var div = 2 * ((p0.X * (p2.Y - p1.Y)) + (p1.X * (p0.Y - p2.Y)) + (p2.X * (p1.Y - p0.Y)));
+ var aux1 = dA * (p2.Y - p1.Y)
+ + dB * (p0.Y - p2.Y)
+ + dC * (p1.Y - p0.Y);
+ var aux2 = -(dA * (p2.X - p1.X)
+ + dB * (p0.X - p2.X)
+ + dC * (p1.X - p0.X));
+ var div = 2 * (p0.X * (p2.Y - p1.Y)
+ + p1.X * (p0.Y - p2.Y)
+ + p2.X * (p1.Y - p0.Y));
- if (div == 0)
- throw new Exception();
+ if (Math.Abs(div) < Settings.Tolerance)
+ throw new Exception("Divisor too small");
var center = new DelaunayPoint(aux1 / div, aux2 / div);
this.Circumcenter = center;
- this.RadiusSquared = ((center.X - p0.X) * (center.X - p0.X)) + ((center.Y - p0.Y) * (center.Y - p0.Y));
- }
-
- public bool IsCounterClockwise(DelaunayPoint point1, DelaunayPoint point2, DelaunayPoint point3)
- {
- var result = ((point2.X - point1.X) * (point3.Y - point1.Y)) -
- ((point3.X - point1.X) * (point2.Y - point1.Y));
- return result > 0;
+ this.RadiusSquared = (center.X - p0.X) * (center.X - p0.X)
+ + (center.Y - p0.Y) * (center.Y - p0.Y);
}
- public bool SharesEdgeWidth(DelaunayTriangle triangle)
- {
- var sharedCount = 0;
- foreach (var vertex in this.Vertices)
- foreach (var vertex2 in triangle.Vertices)
- if (vertex == vertex2)
- sharedCount++;
- return sharedCount == 2;
-
- throw new NotImplementedException();
- }
- public bool IsPointInsideCircumcircle(DelaunayPoint point)
+ private bool SharesEdgeWidth(DelaunayTriangle triangle)
{
- var d_squared = ((point.X - this.Circumcenter.X) * (point.X - this.Circumcenter.X)) +
- ((point.Y - this.Circumcenter.Y) * (point.Y - this.Circumcenter.Y));
- return d_squared < this.RadiusSquared;
+ var shared =
+ from pt1 in this.Vertices
+ from pt2 in triangle.Vertices
+ where pt1 == pt2
+ select pt1;
+ return shared.Count() == 2;
}
}
}
\ No newline at end of file
diff --git a/src/Geometry/2D/Line2d.cs b/src/Geometry/2D/Line2d.cs
index 3c92386..a31f523 100644
--- a/src/Geometry/2D/Line2d.cs
+++ b/src/Geometry/2D/Line2d.cs
@@ -19,6 +19,7 @@ public Line2d(Point2d startPoint, Point2d endPoint)
this.Domain = new Interval(0, this.Length);
}
+
///
/// Initializes a new instance of the class.
///
@@ -28,6 +29,7 @@ public Line2d(Point2d startPoint, Point2d endPoint)
public Line2d(Point2d startPoint, Vector2d direction)
: this(startPoint, startPoint + direction) { }
+
///
/// Initializes a new instance of the class.
///
@@ -38,52 +40,44 @@ public Line2d(Point2d startPoint, Vector2d direction)
public Line2d(Point2d startPoint, Vector2d direction, double length)
: this(startPoint, direction.Unit() * length) { }
+
///
/// Gets or sets the start point of the line.
///
/// 3D Point.
- public Point2d StartPoint
- {
- get;
- set;
- }
+ public Point2d StartPoint { get; set; }
///
/// Gets or sets the end point of the line.
///
/// 3D Point.
- public Point2d EndPoint
- {
- get;
- set;
- }
+ public Point2d EndPoint { get; set; }
///
/// Gets or sets the line's domain.
///
/// Interval.
- public Interval Domain
- {
- get;
- set;
- }
+ public Interval Domain { get; set; }
///
/// Gets the vector representation of the line.
///
- public Vector2d Vector => this; // Implicit line to vector conversion (this property exists just for convenience and readability)
+ public Vector2d Vector =>
+ this; // Implicit line to vector conversion (this property exists just for convenience and readability)
///
/// Gets the length of the line.
///
public double Length => this.Vector.Length;
+
///
/// Implicit conversion from line to vector.
///
/// Line to be transformed into vector.
public static implicit operator Vector2d(Line2d line) => line.EndPoint - line.StartPoint;
+
///
/// Computes if a given point is at the left, right or on the current line.
///
diff --git a/src/Geometry/2D/Point2d.cs b/src/Geometry/2D/Point2d.cs
index 7c37e8a..212fb22 100644
--- a/src/Geometry/2D/Point2d.cs
+++ b/src/Geometry/2D/Point2d.cs
@@ -13,6 +13,7 @@ public class Point2d
public Point2d()
: this(0, 0) { }
+
///
/// Initializes a new instance of the class from x and y coordinates.
///
@@ -24,6 +25,7 @@ public Point2d(double x, double y)
this.Y = y;
}
+
///
/// Initializes a new instance of the class from an existing point.
///
@@ -31,23 +33,16 @@ public Point2d(double x, double y)
public Point2d(Point2d pt)
: this(pt.X, pt.Y) { }
+
///
/// Gets or sets the X coordinate of the point.
///
- public double X
- {
- get;
- set;
- }
+ public double X { get; set; }
///
/// Gets or sets the Y coordinate of the point.
///
- public double Y
- {
- get;
- set;
- }
+ public double Y { get; set; }
///
/// Gets a new 2d point with all coordinates =0.
@@ -57,12 +52,14 @@ public double Y
// Overrided methods
+
///
/// String representation of a 2-dimensional point instance.
///
/// Returns string representation of this Point2d instance.
public override string ToString() => "Point2d{ " + this.X + "; " + this.Y + "}";
+
///
/// Compares a Point2d instance to the given objects.
///
@@ -72,11 +69,12 @@ public override bool Equals(object obj)
{
if (!(obj is Point2d))
return false;
- var pt = (Point2d)obj;
+ var pt = ( Point2d ) obj;
return Math.Abs(this.X - pt.X) <= Settings.Tolerance
&& Math.Abs(this.Y - pt.Y) <= Settings.Tolerance;
}
+
///
/// Gets the hash code for the corresponding Point2d instance.
///
@@ -86,11 +84,11 @@ public override int GetHashCode()
unchecked
{
// Choose large primes to avoid hashing collisions
- const int hashingBase = (int)2166136261;
+ const int hashingBase = ( int ) 2166136261;
const int hashingMultiplier = 16777619;
var tol = Settings.Tolerance * 2;
- var tX = (int)(this.X * (1 / tol)) * tol;
- var tY = (int)(this.Y * (1 / tol)) * tol;
+ var tX = ( int ) (this.X * (1 / tol)) * tol;
+ var tY = ( int ) (this.Y * (1 / tol)) * tol;
var hash = hashingBase;
hash = (hash * hashingMultiplier) ^ tX.GetHashCode();
@@ -99,13 +97,16 @@ public override int GetHashCode()
}
}
+
///
/// Add two points together.
///
/// First point.
/// Second point.
/// Addition result.
- public static Vector2d operator +(Point2d point, Point2d point2) => new Vector2d(point.X + point2.X, point.Y + point2.Y);
+ public static Vector2d operator +(Point2d point, Point2d point2) =>
+ new Vector2d(point.X + point2.X, point.Y + point2.Y);
+
///
/// Substracts one point from another.
@@ -113,7 +114,9 @@ public override int GetHashCode()
/// First point.
/// Second point.
/// Substraction result.
- public static Vector2d operator -(Point2d point, Point2d point2) => new Vector2d(point.X - point2.X, point.Y - point2.Y);
+ public static Vector2d operator -(Point2d point, Point2d point2) =>
+ new Vector2d(point.X - point2.X, point.Y - point2.Y);
+
///
/// Negates a given point.
@@ -122,13 +125,16 @@ public override int GetHashCode()
/// Negation result.
public static Point2d operator -(Point2d point) => new Point2d(-point.X, -point.Y);
+
///
/// Multiplies a point by a number.
///
/// Point to multiply.
/// Operand.
/// Multiplication result.
- public static Point2d operator *(Point2d point, double scalar) => new Point2d(point.X * scalar, point.Y * scalar);
+ public static Point2d operator *(Point2d point, double scalar) =>
+ new Point2d(point.X * scalar, point.Y * scalar);
+
///
/// Multiplies a point by a number.
@@ -136,7 +142,9 @@ public override int GetHashCode()
/// Operand.
/// Point to multiply.
/// Multiplication result.
- public static Point2d operator *(double scalar, Point2d point) => new Point2d(point.X * scalar, point.Y * scalar);
+ public static Point2d operator *(double scalar, Point2d point) =>
+ new Point2d(point.X * scalar, point.Y * scalar);
+
///
/// Divides a point by a number.
@@ -144,7 +152,9 @@ public override int GetHashCode()
/// Point.
/// Operand.
/// Division result.
- public static Point2d operator /(Point2d point, double scalar) => new Point2d(point.X / scalar, point.Y / scalar);
+ public static Point2d operator /(Point2d point, double scalar) =>
+ new Point2d(point.X / scalar, point.Y / scalar);
+
///
/// Equality comparison between points.
@@ -154,6 +164,7 @@ public override int GetHashCode()
/// True if equal.
public static bool operator ==(Point2d point, Point2d point2) => point.Equals(point2);
+
///
/// Inequality comparison between points.
///
@@ -162,22 +173,27 @@ public override int GetHashCode()
/// True if NOT equal.
public static bool operator !=(Point2d point, Point2d point2) => !point.Equals(point2);
+
///
/// Divides a point by a number.
///
/// Point.
/// Vector.
/// Division result.
- public static Point2d operator +(Point2d point, Vector2d v) => new Point2d(point.X + v.X, point.Y + v.Y);
+ public static Point2d operator +(Point2d point, Vector2d v) =>
+ new Point2d(point.X + v.X, point.Y + v.Y);
+
// Implicit conversions
+
///
/// Explicit conversion from 2-dimensional point to vector.
///
/// Vector to convert.
public static explicit operator Point2d(Vector2d v) => new Point2d(v.X, v.Y);
+
///
/// Implicit conversion from 2-dimensional point to vector.
///
diff --git a/src/Geometry/2D/Polyline2d.cs b/src/Geometry/2D/Polyline2d.cs
index c031ceb..6197355 100644
--- a/src/Geometry/2D/Polyline2d.cs
+++ b/src/Geometry/2D/Polyline2d.cs
@@ -15,6 +15,7 @@ public class Polyline2d
private bool segmentsNeedUpdate;
private List vertices;
+
///
/// Initializes a new instance of the class.
///
@@ -23,24 +24,17 @@ public class Polyline2d
public Polyline2d(List vertices, bool closed)
{
this.vertices = vertices;
- this.IsClosed = closed; // Call the property (not the field), to have if add the first point at the end if necessary.
+ this.IsClosed =
+ closed; // Call the property (not the field), to have if add the first point at the end if necessary.
this.RebuildSegments();
}
+
///
/// Gets or sets the polyline vertices.
///
/// List of vertices.
- public List Vertices
- {
- get => this.vertices;
-
- set
- {
- this.vertices = value;
- this.segmentsNeedUpdate = true;
- }
- }
+ public List Vertices => this.vertices;
///
/// Gets the polyline segments.
@@ -103,6 +97,7 @@ public bool IsClosed
}
}
+
///
/// Computes the area of the polyline.
///
@@ -126,6 +121,7 @@ public double Area()
return area / 2.0;
}
+
///
/// Checks if the current polyline is CW or CCW.
///
@@ -148,11 +144,10 @@ public bool IsClockwise()
{
if (this.vertices[i].Y > ymin)
continue;
- if (this.vertices[i].Y == ymin)
- // just as low
- if (this.vertices[i].X < xmin)
- // and to left
- continue;
+
+ if (Math.Abs(this.vertices[i].Y - ymin) < Settings.Tolerance
+ && this.vertices[i].X < xmin)
+ continue;
rmin = i; // a new rightmost lowest vertex
xmin = this.vertices[i].X;
@@ -163,35 +158,44 @@ public bool IsClockwise()
// ccw <=> the edge leaving V[rmin] is left of the entering edge
double result;
if (rmin == 0)
- result = new Line2d(this.vertices[this.vertices.Count - 1], this.vertices[0]).IsLeft(this.vertices[1]);
+ {
+ result =
+ new Line2d(this.vertices[this.vertices.Count - 1], this.vertices[0]).IsLeft(
+ this.vertices[1]);
+ }
else
- result = new Line2d(this.vertices[rmin - 1], this.vertices[rmin]).IsLeft(this.vertices[rmin + 1]);
+ {
+ result = new Line2d(this.vertices[rmin - 1], this.vertices[rmin]).IsLeft(
+ this.vertices[rmin + 1]);
+ }
if (result == 0)
throw new Exception("Polyline is degenerate, cannot compute orientation.");
return result < 0;
}
+
///
/// Reparametrizes the current curve to a unit interval.
///
public void Reparametrize()
{
- var maxParameter = this.domain.End;
var ratio = 1 / this.domain.End;
double currentParam = 0;
- this.segments.ForEach(segment =>
- {
- var nextParam = currentParam + (segment.Domain.Length * ratio);
- segment.Domain = new Interval(currentParam, nextParam);
- currentParam = nextParam;
- });
+ this.segments.ForEach(
+ segment =>
+ {
+ var nextParam = currentParam + segment.Domain.Length * ratio;
+ segment.Domain = new Interval(currentParam, nextParam);
+ currentParam = nextParam;
+ });
this.domain = Interval.Unit;
}
+
private void RebuildSegments()
{
this.segments = new List();
@@ -207,6 +211,7 @@ private void RebuildSegments()
this.domain = new Interval(0, currentParam);
}
+
private Line2d BuildSegment(ref double currentParam, Point2d vertA, Point2d vertB)
{
var line = new Line2d(vertA, vertB);
diff --git a/src/Geometry/2D/Ray2d.cs b/src/Geometry/2D/Ray2d.cs
index 62ae723..7250834 100644
--- a/src/Geometry/2D/Ray2d.cs
+++ b/src/Geometry/2D/Ray2d.cs
@@ -18,24 +18,17 @@ public Ray2d(Point2d origin, Vector2d direction)
this.Direction = direction ?? throw new ArgumentNullException(nameof(direction));
}
+
///
/// Gets or sets the origin of the ray.
///
/// Origin point.
- public Point2d Origin
- {
- get;
- set;
- }
+ public Point2d Origin { get; set; }
///
/// Gets or sets the direction of the ray as a unit vector.
///
/// Direction vector.
- public Vector2d Direction
- {
- get;
- set;
- }
+ public Vector2d Direction { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Geometry/2D/Vector2d.cs b/src/Geometry/2D/Vector2d.cs
index e473bf5..1986323 100644
--- a/src/Geometry/2D/Vector2d.cs
+++ b/src/Geometry/2D/Vector2d.cs
@@ -15,6 +15,7 @@ public class Vector2d
public Vector2d(Vector2d vector)
: this(vector.X, vector.Y) { }
+
///
/// Initializes a new instance of the class from a point.
///
@@ -23,6 +24,7 @@ public Vector2d(Vector2d vector)
public Vector2d(Point2d point)
: this(point.X, point.Y) { }
+
///
/// Initializes a new instance of the class.
///
@@ -34,30 +36,23 @@ public Vector2d(double x, double y)
this.Y = y;
}
+
///
/// Gets or sets the X Coordininate of the vector.
///
/// X coordinate.
- public double X
- {
- get;
- set;
- }
+ public double X { get; set; }
///
/// Gets or sets the Y coordinate of the vector.
///
/// Y coordinate.
- public double Y
- {
- get;
- set;
- }
+ public double Y { get; set; }
///
/// Gets the squared length of the vector.
///
- public double LengthSquared => (this.X * this.X) + (this.Y * this.Y);
+ public double LengthSquared => this.X * this.X + this.Y * this.Y;
///
/// Gets the length of the vector.
@@ -74,12 +69,14 @@ public double Y
///
public static Vector2d WorldY => new Vector2d(0, 1);
+
///
/// Returns a unit vector of this vector.
///
/// New vector of unit lenght.
public Vector2d Unit() => new Vector2d(this / this.Length);
+
///
/// Force this vector to be unit length.
///
@@ -90,53 +87,65 @@ public void Unitize()
this.Y /= length;
}
+
///
/// Returns a CCW perpendicular vector to the current instance.
///
///
public Vector2d Perp() => new Vector2d(-this.Y, this.X);
+
///
/// Computes the dot product between this vector and the given one.
///
/// Vector to compute dot-product with.
/// Dot product result.
- public double DotProduct(Vector2d vector) => (this.X * vector.X) + (this.Y * vector.Y);
+ public double DotProduct(Vector2d vector) => this.X * vector.X + this.Y * vector.Y;
+
///
/// Computes the perp product between this vector and the given one.
///
/// Vector to compute perp-product with.
/// Perp product result.
- public double PerpProduct(Vector2d vector) => (this.X * vector.Y) - (this.Y * vector.X);
+ public double PerpProduct(Vector2d vector) => this.X * vector.Y - this.Y * vector.X;
+
///
/// Sums two vectors together.
///
/// Vector A.
/// Vector B.
- public static Vector2d operator +(Vector2d v, Vector2d v2) => new Vector2d(v.X + v2.X, v.Y + v2.Y);
+ public static Vector2d operator +(Vector2d v, Vector2d v2) =>
+ new Vector2d(v.X + v2.X, v.Y + v2.Y);
+
///
/// Substracts one vector from another.
///
/// Vector A.
/// Vector B.
- public static Vector2d operator -(Vector2d v, Vector2d v2) => new Vector2d(v.X - v2.X, v.Y - v2.Y);
+ public static Vector2d operator -(Vector2d v, Vector2d v2) =>
+ new Vector2d(v.X - v2.X, v.Y - v2.Y);
+
///
/// Multiplies a vector with a number.
///
/// Vector.
/// Number to multiply vector with.
- public static Vector2d operator *(Vector2d v, double scalar) => new Vector2d(v.X * scalar, v.Y * scalar);
+ public static Vector2d operator *(Vector2d v, double scalar) =>
+ new Vector2d(v.X * scalar, v.Y * scalar);
+
///
/// Multiplies a vector with a number.
///
/// Number to multiply vector with.
/// Vector.
- public static Vector2d operator *(double scalar, Vector2d v) => new Vector2d(v.X * scalar, v.Y * scalar);
+ public static Vector2d operator *(double scalar, Vector2d v) =>
+ new Vector2d(v.X * scalar, v.Y * scalar);
+
///
/// Negates the values of the vector.
@@ -144,12 +153,15 @@ public void Unitize()
/// Vector.
public static Vector2d operator -(Vector2d v) => new Vector2d(-v.X, -v.Y);
+
///
/// Divides a vector with a number.
///
/// Vector.
/// Number to divide vector with.
- public static Vector2d operator /(Vector2d v, double scalar) => new Vector2d(v.X / scalar, v.Y / scalar);
+ public static Vector2d operator /(Vector2d v, double scalar) =>
+ new Vector2d(v.X / scalar, v.Y / scalar);
+
///
/// Checks for equality between two vectors.
@@ -158,6 +170,7 @@ public void Unitize()
/// Vector B.
public static bool operator ==(Vector2d v, Vector2d w) => v.Equals(w);
+
///
/// Checks for inequality between two vectors.
///
@@ -165,12 +178,14 @@ public void Unitize()
/// Vector B.
public static bool operator !=(Vector2d v, Vector2d w) => !v.Equals(w);
+
///
/// Gets the string representation of the vector.
///
///
public override string ToString() => $"Vector3d [{this.X}, {this.Y}]";
+
///
/// Checks if the vector is equal to an object.
///
@@ -186,6 +201,7 @@ public override bool Equals(object obj)
&& Math.Abs(this.Y - vect.Y) <= Settings.Tolerance;
}
+
///
/// Get the hashCode of the vector.
///
@@ -196,11 +212,11 @@ public override int GetHashCode()
{
// Choose large primes to avoid hashing collisions
// Choose large primes to avoid hashing collisions
- const int hashingBase = (int)2166136261;
+ const int hashingBase = ( int ) 2166136261;
const int hashingMultiplier = 16777619;
var tol = Settings.Tolerance * 2;
- var tX = (int)(this.X * (1 / tol)) * tol;
- var tY = (int)(this.Y * (1 / tol)) * tol;
+ var tX = ( int ) (this.X * (1 / tol)) * tol;
+ var tY = ( int ) (this.Y * (1 / tol)) * tol;
var hash = hashingBase;
hash = (hash * hashingMultiplier) ^ tX.GetHashCode();
diff --git a/src/Geometry/3D/Circle.cs b/src/Geometry/3D/Circle.cs
index 7025083..b76bcb1 100644
--- a/src/Geometry/3D/Circle.cs
+++ b/src/Geometry/3D/Circle.cs
@@ -3,17 +3,35 @@
namespace Paramdigma.Core.Geometry
{
+ ///
+ /// Represents a planar circle curve.
+ ///
public class Circle : ICurve
{
+ ///
+ /// The base plane for the circle.
+ ///
public Plane Plane;
+
+ ///
+ /// The radius of the circle.
+ ///
public double Radius;
+
+ ///
+ /// Initializes a new instance of by it's plane and radius.
+ ///
+ /// The plane to draw the circle at.
+ /// The desired radius of the circle.
public Circle(Plane plane, double radius)
{
this.Plane = plane;
this.Radius = radius;
}
+
+ ///
public Point3d PointAt(double t)
{
var radians = t * 2 * Math.PI;
@@ -22,13 +40,24 @@ public Point3d PointAt(double t)
return this.Plane.PointAt(x, y, 0);
}
+
+ ///
public Vector3d TangentAt(double t) => this.NormalAt(t).Cross(this.Plane.ZAxis);
+
+ ///
public Vector3d NormalAt(double t) => (this.Plane.Origin - this.PointAt(t)).Unit();
+
+ ///
public Vector3d BinormalAt(double t) => this.TangentAt(t).Cross(this.NormalAt(t));
- public Plane FrameAt(double t) => new Plane(this.PointAt(t), this.NormalAt(t), this.BinormalAt(t), this.TangentAt(t));
+ ///
+ public Plane FrameAt(double t) => new Plane(
+ this.PointAt(t),
+ this.NormalAt(t),
+ this.BinormalAt(t),
+ this.TangentAt(t));
}
}
\ No newline at end of file
diff --git a/src/Geometry/3D/Line.cs b/src/Geometry/3D/Line.cs
index 959b4b4..0a317c9 100644
--- a/src/Geometry/3D/Line.cs
+++ b/src/Geometry/3D/Line.cs
@@ -18,8 +18,10 @@ public Line(Point3d startPoint, Point3d endPoint)
this.EndPoint = endPoint;
}
+
///
- /// Initializes a new instance of the class from an origin point, a direction and a specified
+ /// Initializes a new instance of the class from an origin point, a direction
+ /// and a specified
/// length.
///
/// Start point of the line.
@@ -28,26 +30,20 @@ public Line(Point3d startPoint, Point3d endPoint)
public Line(Point3d origin, Vector3d direction, double length)
{
this.StartPoint = origin;
- this.EndPoint = origin + (direction.Unit() * length);
+ this.EndPoint = origin + direction.Unit() * length;
}
+
///
/// Gets or sets the lines's start point.
///
- public Point3d StartPoint
- {
- get;
- set;
- }
+ public Point3d StartPoint { get; set; }
///
/// Gets or sets the line's end point.
///
- public Point3d EndPoint
- {
- get;
- set;
- }
+ public Point3d EndPoint { get; set; }
+
///
/// Checks if line is valid.
@@ -55,12 +51,15 @@ public Point3d EndPoint
/// True if valid.
public override bool CheckValidity() => this.Length >= Settings.Tolerance;
+
///
/// Computes thepoint at the given parameter.
///
/// Parameter of the point. Must be between 0 and 1.
/// Point at specified parameter.
- public override Point3d PointAt(double t) => this.StartPoint + (this.Domain.RemapToUnit(t) * (this.EndPoint - this.StartPoint));
+ public override Point3d PointAt(double t) =>
+ this.StartPoint + this.Domain.RemapToUnit(t) * (this.EndPoint - this.StartPoint);
+
///
/// Computes the tangent at the given parameter.
@@ -74,6 +73,7 @@ public override Vector3d TangentAt(double t)
return tangent;
}
+
///
/// Computes the normal at the given parameter.
///
@@ -82,29 +82,33 @@ public override Vector3d TangentAt(double t)
public override Vector3d NormalAt(double t)
{
var tangent = this.TangentAt(t);
- var v = new Vector3d();
-
- if (Math.Abs(tangent.Dot(Vector3d.UnitZ) - 1) < Settings.Tolerance)
- v = Vector3d.UnitX;
- else
- v = Vector3d.UnitZ;
-
+ var v = Math.Abs(tangent.Dot(Vector3d.UnitZ) - 1) < Settings.Tolerance
+ ? Vector3d.UnitX
+ : Vector3d.UnitZ;
return tangent.Cross(v);
}
+
///
/// Computes the bi-normal vector at the given parameter.
///
/// Parameter of the bi-normal vector. Must be between 0 and 1.
/// Bi-normal vector at specified parameter.
- public override Vector3d BinormalAt(double t) => Vector3d.CrossProduct(this.TangentAt(t), this.NormalAt(t));
+ public override Vector3d BinormalAt(double t) =>
+ Vector3d.CrossProduct(this.TangentAt(t), this.NormalAt(t));
+
///
/// Computes the perpendicular frame at the given parameter.
///
/// Parameter of the frame. Must be between 0 and 1.
/// Frame at specified parameter.
- public override Plane FrameAt(double t) => new Plane(this.PointAt(t), this.TangentAt(t), this.NormalAt(t), this.BinormalAt(t));
+ public override Plane FrameAt(double t) => new Plane(
+ this.PointAt(t),
+ this.TangentAt(t),
+ this.NormalAt(t),
+ this.BinormalAt(t));
+
///
/// Computes the length of the line.
@@ -112,6 +116,12 @@ public override Vector3d NormalAt(double t)
/// Line length.
protected override double ComputeLength() => this.StartPoint.DistanceTo(this.EndPoint);
+
+ ///
+ /// Explicitly converts a line to it's vector representation.
+ ///
+ /// Line to convert.
+ /// Vector defining the line direction and length.
public static explicit operator Vector3d(Line line) => line.EndPoint - line.StartPoint;
}
}
\ No newline at end of file
diff --git a/src/Geometry/3D/Mesh/Mesh.cs b/src/Geometry/3D/Mesh/Mesh.cs
index 97539d8..ccefdf0 100644
--- a/src/Geometry/3D/Mesh/Mesh.cs
+++ b/src/Geometry/3D/Mesh/Mesh.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using Paramdigma.Core.Geometry;
namespace Paramdigma.Core.HalfEdgeMesh
@@ -21,6 +22,7 @@ public Mesh()
this.Boundaries = new List();
}
+
///
/// Initializes a new instance of the class from verticees and faces.
///
@@ -34,9 +36,12 @@ public Mesh(List vertices, List> faceIndexes)
this.CreateVertices(vertices);
// - Iterate through faces, creating face, edge, and halfedge objects (and connecting where possible)
- this.CreateFaces(faceIndexes);
+ var result = this.CreateFaces(faceIndexes);
+ if (!result)
+ throw new Exception("Couldn't create faces for this mesh");
}
+
///
/// Initializes a new instance of the class from verticees and faces.
///
@@ -51,6 +56,7 @@ public Mesh(List vertices, List> faceIndexes)
this.CreateFaces(faceIndexes);
}
+
///
/// Initializes a new instance of the class from an existing one.
///
@@ -65,64 +71,42 @@ public Mesh(Mesh halfEdgeMesh)
this.Boundaries = new List(halfEdgeMesh.Boundaries);
}
+
///
/// Gets or sets the vertices of the mesh.
///
- public List Vertices
- {
- get;
- set;
- }
+ public List Vertices { get; set; }
///
/// Gets or sets the edges of the mesh.
///
- public List Edges
- {
- get;
- set;
- }
+ public List Edges { get; set; }
///
/// Gets or sets the faces of the mesh.
///
- public List Faces
- {
- get;
- set;
- }
+ public List Faces { get; set; }
///
/// Gets or sets the corners of the mesh.
///
- public List Corners
- {
- get;
- set;
- }
+ public List Corners { get; set; }
///
/// Gets or sets the half-edges of the mesh.
///
- public List HalfEdges
- {
- get;
- set;
- }
+ public List HalfEdges { get; set; }
///
/// Gets or sets the boundaries of the mesh.
///
- public List Boundaries
- {
- get;
- set;
- }
+ public List Boundaries { get; set; }
///
/// Gets the euler characteristic of the mesh.
///
- public int EulerCharacteristic => (this.Vertices.Count - this.Edges.Count) + this.Faces.Count;
+ public int EulerCharacteristic => this.Vertices.Count - this.Edges.Count + this.Faces.Count;
+
///
/// Check if the mesh has isolated vertices.
@@ -131,12 +115,15 @@ public List Boundaries
public bool HasIsolatedVertices()
{
foreach (var v in this.Vertices)
+ {
if (v.IsIsolated())
return true;
+ }
return false;
}
+
///
/// Check if the mesh contains isolated faces.
///
@@ -148,8 +135,10 @@ public bool HasIsolatedFaces()
var boundaryEdges = 0;
var adjacent = f.AdjacentHalfEdges();
foreach (var e in adjacent)
+ {
if (e.OnBoundary)
boundaryEdges++;
+ }
if (boundaryEdges == adjacent.Count)
return true;
@@ -158,6 +147,7 @@ public bool HasIsolatedFaces()
return false;
}
+
///
/// Check if the mesh contains non-manifold edges.
///
@@ -165,12 +155,15 @@ public bool HasIsolatedFaces()
public bool HasNonManifoldEdges()
{
foreach (var edge in this.Edges)
+ {
if (edge.AdjacentFaces().Count > 2)
return true;
+ }
return false;
}
+
///
/// Assign an index number to each mesh member.
///
@@ -219,6 +212,7 @@ public void IndexElements()
}
}
+
///
/// Assign an index to each vertex of the mesh.
///
@@ -232,6 +226,7 @@ public Dictionary IndexVertices()
return index;
}
+
///
/// Assign an index to each face of the mesh.
///
@@ -245,6 +240,7 @@ public Dictionary IndexFaces()
return index;
}
+
///
/// Assign an index to each edge of the mesh.
///
@@ -258,6 +254,7 @@ public Dictionary IndexEdges()
return index;
}
+
///
/// Assign an index to each Half-Edge of the mesh.
///
@@ -271,6 +268,7 @@ public Dictionary IndexHalfEdes()
return index;
}
+
///
/// Assign an index to each corner of the mesh.
///
@@ -284,24 +282,28 @@ public Dictionary IndexCorners()
return index;
}
+
///
/// Check if a mesh is triangular.
///
/// Returns true if all faces are triangular.
public bool IsTriangularMesh() => this.IsMesh() == IsMeshResult.Triangular;
+
///
/// Check if a mesh is quad.
///
/// Returns true if all faces are quads.
public bool IsQuadMesh() => this.IsMesh() == IsMeshResult.Quad;
+
///
/// Check if a mesh is n-gonal.
///
/// Returns true if the mesh contains ANY ngons.
public bool IsNgonMesh() => this.IsMesh() == IsMeshResult.Ngon;
+
///
/// Returns an enum corresponding to the mesh face topology (triangular, quad or ngon).
///
@@ -314,9 +316,10 @@ private IsMeshResult IsMesh()
return IsMeshResult.Quad;
if (count.Ngons != 0)
return IsMeshResult.Ngon;
- return IsMeshResult.ERROR;
+ return IsMeshResult.Error;
}
+
///
/// Get human readable description of this mesh.
///
@@ -325,8 +328,10 @@ public string GetMeshInfo()
{
const string head = "--- Mesh Info ---\n";
- var vef = "V: " + this.Vertices.Count + "; F: " + this.Faces.Count + "; E:" + this.Edges.Count + "\n";
- var hec = "Half-edges: " + this.HalfEdges.Count + "; Corners: " + this.Corners.Count + "\n";
+ var vef = "V: " + this.Vertices.Count + "; F: " + this.Faces.Count + "; E:"
+ + this.Edges.Count + "\n";
+ var hec = "Half-edges: " + this.HalfEdges.Count + "; Corners: " + this.Corners.Count
+ + "\n";
var bounds = "Boundaries: " + this.Boundaries.Count + "\n";
var euler = "Euler characteristic: " + this.EulerCharacteristic + "\n";
var isoVert = "Isolated vertices: " + this.HasIsolatedVertices() + "\n";
@@ -340,19 +345,24 @@ public string GetMeshInfo()
const string tail = "----- -----\n\n";
- return head + vef + hec + bounds + euler + isoVert + isoFace + manifold + triangles + quads + ngons + tail;
+ return head + vef + hec + bounds + euler + isoVert + isoFace + manifold + triangles
+ + quads + ngons + tail;
}
+
///
/// Gets string representation of the mesh.
///
/// Mesh string.
public override string ToString()
{
- var vefh = "V: " + this.Vertices.Count + "; F: " + this.Faces.Count + "; E:" + this.Edges.Count + "; hE: " + this.HalfEdges.Count;
+ var vefh = "V: " + this.Vertices.Count + "; F: " + this.Faces.Count + "; E:"
+ + this.Edges.Count
+ + "; hE: " + this.HalfEdges.Count;
return "HE_Mesh{" + vefh + "}";
}
+
private void CreateVertices(List points)
{
var verts = new List(points.Count);
@@ -366,8 +376,9 @@ private void CreateVertices(List points)
this.Vertices = verts;
}
+
// Takes a List containing another List per face with the vertex indexes belonging to that face
- private bool CreateFaces(List> faceIndexes)
+ private bool CreateFaces(IEnumerable> faceIndexes)
{
var edgeCount = new Dictionary();
var existingHalfEdges = new Dictionary();
@@ -399,7 +410,7 @@ private bool CreateFaces(List> faceIndexes)
// Set previous and next
h.Next = tempHEdges[(i + 1) % indexes.Count];
- h.Prev = tempHEdges[((i + indexes.Count) - 1) % indexes.Count];
+ h.Prev = tempHEdges[(i + indexes.Count - 1) % indexes.Count];
h.OnBoundary = false;
hasTwinHalfEdge.Add(h, false);
@@ -459,46 +470,46 @@ private bool CreateFaces(List> faceIndexes)
this.Boundaries.Add(f);
var boundaryCycle = new List();
- var hE = h;
+ var halfEdge = h;
do
{
- var bH = new MeshHalfEdge();
- this.HalfEdges.Add(bH);
- boundaryCycle.Add(bH);
+ var boundaryHalfEdge = new MeshHalfEdge();
+ this.HalfEdges.Add(boundaryHalfEdge);
+ boundaryCycle.Add(boundaryHalfEdge);
- var nextHE = hE.Next;
- while (hasTwinHalfEdge[nextHE])
- nextHE = nextHE.Twin.Next;
+ var nextHalfEdge = halfEdge.Next;
+ while (hasTwinHalfEdge[nextHalfEdge])
+ nextHalfEdge = nextHalfEdge.Twin.Next;
- bH.Vertex = nextHE.Vertex;
- bH.Edge = hE.Edge;
- bH.OnBoundary = true;
+ boundaryHalfEdge.Vertex = nextHalfEdge.Vertex;
+ boundaryHalfEdge.Edge = halfEdge.Edge;
+ boundaryHalfEdge.OnBoundary = true;
- bH.Face = f;
- f.HalfEdge = bH;
+ boundaryHalfEdge.Face = f;
+ f.HalfEdge = boundaryHalfEdge;
- bH.Twin = hE;
- hE.Twin = bH;
+ boundaryHalfEdge.Twin = halfEdge;
+ halfEdge.Twin = boundaryHalfEdge;
- hE = nextHE;
- } while (hE != h);
+ halfEdge = nextHalfEdge;
+ } while (halfEdge != h);
var n = boundaryCycle.Count;
for (var j = 0; j < n; j++)
{
- boundaryCycle[j].Next = boundaryCycle[((j + n) - 1) % n];
+ boundaryCycle[j].Next = boundaryCycle[(j + n - 1) % n];
boundaryCycle[j].Prev = boundaryCycle[(j + 1) % n];
hasTwinHalfEdge[boundaryCycle[j]] = true;
hasTwinHalfEdge[boundaryCycle[j].Twin] = true;
}
}
- if (!h.OnBoundary)
- {
- var corner = new MeshCorner {HalfEdge = h};
- h.Corner = corner;
- this.Corners.Add(corner);
- }
+ if (h.OnBoundary)
+ continue;
+
+ var corner = new MeshCorner {HalfEdge = h};
+ h.Corner = corner;
+ this.Corners.Add(corner);
}
// Check mesh for common errors
@@ -511,11 +522,13 @@ private bool CreateFaces(List> faceIndexes)
return true;
}
+
private FaceData CountFaceEdges()
{
FaceData data = default;
foreach (var face in this.Faces)
+ {
switch (face.AdjacentCorners().Count)
{
case 3:
@@ -528,17 +541,18 @@ private FaceData CountFaceEdges()
data.Ngons++;
break;
}
+ }
return data;
}
+
///
/// Type of mesh (Triangular, Quad, Ngon or Error).
///
private enum IsMeshResult
{
- Triangular, Quad, Ngon,
- ERROR
+ Triangular, Quad, Ngon, Error
}
private struct FaceData
diff --git a/src/Geometry/3D/Mesh/MeshCorner.cs b/src/Geometry/3D/Mesh/MeshCorner.cs
index 55be38c..f53d78c 100644
--- a/src/Geometry/3D/Mesh/MeshCorner.cs
+++ b/src/Geometry/3D/Mesh/MeshCorner.cs
@@ -10,23 +10,16 @@ public class MeshCorner
///
public MeshCorner() => this.Index = -1;
+
///
/// Gets or sets the corner's first half-edge.
///
- public MeshHalfEdge HalfEdge
- {
- get;
- set;
- }
+ public MeshHalfEdge HalfEdge { get; set; }
///
/// Gets or sets the index of the mesh corner.
///
- public int Index
- {
- get;
- set;
- }
+ public int Index { get; set; }
///
/// Gets the mesh corner vertex.
diff --git a/src/Geometry/3D/Mesh/MeshEdge.cs b/src/Geometry/3D/Mesh/MeshEdge.cs
index 6fe0c35..58a5ea3 100644
--- a/src/Geometry/3D/Mesh/MeshEdge.cs
+++ b/src/Geometry/3D/Mesh/MeshEdge.cs
@@ -13,29 +13,23 @@ public class MeshEdge
///
public MeshEdge() => this.Index = -1;
+
///
/// Gets or sets the half-edge linked to this edge.
///
- public MeshHalfEdge HalfEdge
- {
- get;
- set;
- }
+ public MeshHalfEdge HalfEdge { get; set; }
///
/// Gets or sets the index of this Mesh Edge.
///
- public int Index
- {
- get;
- set;
- }
+ public int Index { get; set; }
///
/// Gets a value indicating whether the mesh edge lies on a boundary.
///
public bool OnBoundary => this.HalfEdge.OnBoundary || this.HalfEdge.Twin.OnBoundary;
+
///
/// Gets the adjacent vertices of this given edge.
///
@@ -46,16 +40,21 @@ public List AdjacentVertices()
return vertices;
}
+
///
/// Gets the adjacent faces of this edge.
///
///
public List AdjacentFaces()
{
- var faces = new List {this.HalfEdge.AdjacentFace, this.HalfEdge.Twin.AdjacentFace};
+ var faces = new List
+ {
+ this.HalfEdge.AdjacentFace, this.HalfEdge.Twin.AdjacentFace
+ };
return faces;
}
+
///
/// Gets the adjacent edges of this edge.
///
diff --git a/src/Geometry/3D/Mesh/MeshFace.cs b/src/Geometry/3D/Mesh/MeshFace.cs
index 6026c4c..beb07f4 100644
--- a/src/Geometry/3D/Mesh/MeshFace.cs
+++ b/src/Geometry/3D/Mesh/MeshFace.cs
@@ -17,23 +17,16 @@ public MeshFace()
this.Index = -1;
}
+
///
/// Gets or sets one of the half-edges surrounding the face.
///
- public MeshHalfEdge HalfEdge
- {
- get;
- set;
- }
+ public MeshHalfEdge HalfEdge { get; set; }
///
/// Gets or sets the face index on the mesh face list.
///
- public int Index
- {
- get;
- set;
- }
+ public int Index { get; set; }
///
/// Gets the area of the face.
@@ -47,6 +40,7 @@ public int Index
/// Returns the perpendicular vector to the face.
public Vector3d Normal => MeshGeometry.FaceNormal(this);
+
///
/// Get all adjacent edges to this face.
///
@@ -64,6 +58,7 @@ public List AdjacentEdges()
return edges;
}
+
///
/// Get all adjacent half-edges to this face.
///
@@ -81,6 +76,7 @@ public List AdjacentHalfEdges()
return halfEdges;
}
+
///
/// Get all adjacent vertices to this face.
///
@@ -98,6 +94,7 @@ public List AdjacentVertices()
return vertices;
}
+
///
/// Get all adjacent faces to this face.
///
@@ -116,6 +113,7 @@ public List AdjacentFaces()
return faces;
}
+
///
/// Get all adjacent corners to this face.
///
@@ -133,12 +131,14 @@ public List AdjacentCorners()
return corners;
}
+
///
/// Checks if the current face is a boundary face.
///
/// Returns true if the face is a boundary face, false if not.
public bool IsBoundaryLoop() => this.HalfEdge.OnBoundary;
+
///
/// Convert the mesh face to string.
///
diff --git a/src/Geometry/3D/Mesh/MeshGeometry.cs b/src/Geometry/3D/Mesh/MeshGeometry.cs
index 095d733..84d3b8c 100644
--- a/src/Geometry/3D/Mesh/MeshGeometry.cs
+++ b/src/Geometry/3D/Mesh/MeshGeometry.cs
@@ -13,7 +13,9 @@ public static class MeshGeometry
///
/// The half-edge vector.
/// Half edge.
- public static Vector3d Vector(MeshHalfEdge halfEdge) => halfEdge.Vertex - halfEdge.Next.Vertex;
+ public static Vector3d Vector(MeshHalfEdge halfEdge) =>
+ halfEdge.Vertex - halfEdge.Next.Vertex;
+
///
/// Calculates the length of the specified edge.
@@ -22,6 +24,7 @@ public static class MeshGeometry
/// Edge.
public static double Length(MeshEdge edge) => Vector(edge.HalfEdge).Length;
+
///
/// Calculates the midpoint of the specifiec edge.
///
@@ -35,6 +38,7 @@ public static Point3d MidPoint(MeshEdge edge)
return (a + b) / 2;
}
+
///
/// Calculates the mean edge length of the mesh.
///
@@ -48,6 +52,7 @@ public static double MeanEdgeLength(Mesh mesh)
return sum / mesh.Edges.Count;
}
+
///
/// Computes the area of the specified face.
///
@@ -63,6 +68,7 @@ public static double Area(MeshFace face)
return 0.5 * u.Cross(v).Length;
}
+
///
/// Computes the total area of the mesh.
///
@@ -76,6 +82,7 @@ public static double TotalArea(Mesh mesh)
return sum;
}
+
///
/// Compute the normal vector of the specified face.
///
@@ -91,6 +98,7 @@ public static Vector3d FaceNormal(MeshFace face)
return u.Cross(v).Unit();
}
+
///
/// Compute the centroid of the specified face.
///
@@ -109,6 +117,7 @@ public static Point3d Centroid(MeshFace face)
return (a + b + c) / 3;
}
+
///
/// Compute the circumcenter the specified face.
///
@@ -132,11 +141,12 @@ public static Point3d Circumcenter(MeshFace face)
var u = w.Cross(ab) * ac.LengthSquared;
var v = ac.Cross(w) * ab.LengthSquared;
- var x = (Point3d)(u + v) / (2 * w.LengthSquared);
+ var x = ( Point3d ) (u + v) / (2 * w.LengthSquared);
return x + a;
}
+
///
/// Compute the orthonormal bases of the specified face.
///
@@ -151,6 +161,7 @@ public static Vector3d[] OrthonormalBases(MeshFace face)
return new[] {e1, e2};
}
+
///
/// Compute the angle (in radians) at the specified corner.
///
@@ -164,6 +175,7 @@ public static double Angle(MeshCorner corner)
return Math.Acos(Math.Max(-1, Math.Min(1.0, u.Dot(v))));
}
+
///
/// Computes the cotangent of the angle opposite to a halfedge.
///
@@ -180,6 +192,7 @@ public static double Cotan(MeshHalfEdge hE)
return u.Dot(v) / u.Cross(v).Length;
}
+
///
/// Computes the signed angle (in radians) between the faces adjacent to the specified half-edge.
///
@@ -200,6 +213,7 @@ public static double DihedralAngle(MeshHalfEdge hE)
return Math.Atan2(sinTheta, cosTheta);
}
+
///
/// Computes the barycentric dual area around a given mesh vertex.
///
@@ -213,6 +227,7 @@ public static double BarycentricDualArea(MeshVertex vertex)
return area;
}
+
///
/// Computes the circumcentric dual area around a given mesh vertex.
///
@@ -228,12 +243,13 @@ public static double CircumcentricDualarea(MeshVertex vertex)
var cotAlpha = Cotan(hE.Prev);
var cotBeta = Cotan(hE);
- area += ((u2 * cotAlpha) + (v2 * cotBeta)) / 8;
+ area += (u2 * cotAlpha + v2 * cotBeta) / 8;
}
return area;
}
+
///
/// Computes the equally weighted normal arround the specified vertex.
///
@@ -248,6 +264,7 @@ public static Vector3d VertexNormalEquallyWeighted(MeshVertex vertex)
return n.Unit();
}
+
///
/// Computes the area weighted normal arround the specified vertex.
///
@@ -267,6 +284,7 @@ public static Vector3d VertexNormalAreaWeighted(MeshVertex vertex)
return n.Unit();
}
+
///
/// Computes the angle weighted normal arround the specified vertex.
///
@@ -286,6 +304,7 @@ public static Vector3d VertexNormalAngleWeighted(MeshVertex vertex)
return n.Unit();
}
+
///
/// Computes the gauss curvature weighted normal arround the specified vertex.
///
@@ -296,13 +315,14 @@ public static Vector3d VertexNormalGaussCurvature(MeshVertex vertex)
var n = new Vector3d();
foreach (var hE in vertex.AdjacentHalfEdges())
{
- var weight = (0.5 * DihedralAngle(hE)) / Length(hE.Edge);
+ var weight = 0.5 * DihedralAngle(hE) / Length(hE.Edge);
n -= Vector(hE) * weight;
}
return n.Unit();
}
+
///
/// Computes the mean curvature weighted normal arround the specified vertex.
///
@@ -313,13 +333,14 @@ public static Vector3d VertexNormalMeanCurvature(MeshVertex vertex)
var n = new Vector3d();
foreach (var hE in vertex.AdjacentHalfEdges())
{
- var weight = (0.5 * Cotan(hE)) + Cotan(hE.Twin);
+ var weight = 0.5 * Cotan(hE) + Cotan(hE.Twin);
n -= Vector(hE) * weight;
}
return n.Unit();
}
+
///
/// Computes the sphere inscribed normal arround the specified vertex.
///
@@ -339,6 +360,7 @@ public static Vector3d VertexNormalSphereInscribed(MeshVertex vertex)
return n.Unit();
}
+
///
/// Computes the angle defect at the given vertex.
///
@@ -351,15 +373,18 @@ public static double AngleDefect(MeshVertex vertex)
angleSum += Angle(c);
// if (vertex.OnBoundary()) angleSum = Math.PI - angleSum;
- return vertex.OnBoundary() ? Math.PI - angleSum : (2 * Math.PI) - angleSum;
+ return vertex.OnBoundary() ? Math.PI - angleSum : 2 * Math.PI - angleSum;
}
+
///
/// Compute the Gaussian curvature at the given vertex.
///
/// Vertex to compute Gaussian curvature.
/// Number representing the gaussian curvature at that vertex.
- public static double ScalarGaussCurvature(MeshVertex vertex) => AngleDefect(vertex) / CircumcentricDualarea(vertex);
+ public static double ScalarGaussCurvature(MeshVertex vertex) =>
+ AngleDefect(vertex) / CircumcentricDualarea(vertex);
+
///
/// Compute the Mean curvature at the given vertex.
@@ -374,6 +399,7 @@ public static double ScalarMeanCurvature(MeshVertex vertex)
return sum;
}
+
///
/// Compute the total angle defect of the mesh.
///
@@ -387,6 +413,7 @@ public static double TotalAngleDefect(Mesh mesh)
return totalDefect;
}
+
///
/// Compute the principal curvature scalar values at a given vertes.
///
@@ -398,7 +425,7 @@ public static double[] PrincipalCurvatures(MeshVertex vertex)
var h = ScalarMeanCurvature(vertex) / a;
var k = AngleDefect(vertex) / a;
- var discriminant = (h * h) - k;
+ var discriminant = h * h - k;
if (discriminant > 0)
discriminant = Math.Sqrt(discriminant);
else
diff --git a/src/Geometry/3D/Mesh/MeshHalfEdge.cs b/src/Geometry/3D/Mesh/MeshHalfEdge.cs
index 30bb1dd..18b176a 100644
--- a/src/Geometry/3D/Mesh/MeshHalfEdge.cs
+++ b/src/Geometry/3D/Mesh/MeshHalfEdge.cs
@@ -10,86 +10,51 @@ public class MeshHalfEdge
///
public MeshHalfEdge() => this.Index = -1;
+
///
/// Gets or sets the vertex linked to this half-edge.
///
- public MeshVertex Vertex
- {
- get;
- set;
- }
+ public MeshVertex Vertex { get; set; }
///
/// Gets or sets the edge linked to this half-edge.
///
- public MeshEdge Edge
- {
- get;
- set;
- }
+ public MeshEdge Edge { get; set; }
///
/// Gets or sets the face linked to this half-edge.
///
- public MeshFace Face
- {
- get;
- set;
- }
+ public MeshFace Face { get; set; }
///
/// Gets or sets the corner linked to this half-edge.
///
- public MeshCorner Corner
- {
- get;
- set;
- }
+ public MeshCorner Corner { get; set; }
///
/// Gets or sets the next half-edge in a face.
///
- public MeshHalfEdge Next
- {
- get;
- set;
- }
+ public MeshHalfEdge Next { get; set; }
///
/// Gets or sets the previous half-edge in a face.
///
- public MeshHalfEdge Prev
- {
- get;
- set;
- }
+ public MeshHalfEdge Prev { get; set; }
///
/// Gets or sets the opposite half-edge.
///
- public MeshHalfEdge Twin
- {
- get;
- set;
- }
+ public MeshHalfEdge Twin { get; set; }
///
/// Gets or sets a value indicating whether the half-edge lies on a boundary.
///
- public bool OnBoundary
- {
- get;
- set;
- }
+ public bool OnBoundary { get; set; }
///
/// Gets or sets the half-edge index.
///
- public int Index
- {
- get;
- set;
- }
+ public int Index { get; set; }
///
/// Gets the previous vertex of the half-edge.
@@ -101,6 +66,7 @@ public int Index
///
public MeshFace AdjacentFace => this.Twin.Face;
+
///
/// Gets the string representation of the half-edge.
///
diff --git a/src/Geometry/3D/Mesh/MeshPoint.cs b/src/Geometry/3D/Mesh/MeshPoint.cs
index b264cd4..5629003 100644
--- a/src/Geometry/3D/Mesh/MeshPoint.cs
+++ b/src/Geometry/3D/Mesh/MeshPoint.cs
@@ -22,6 +22,7 @@ public MeshPoint(int faceIndex, double u, double v, double w)
this.W = w;
}
+
///
/// Initializes a new instance of the class.
///
@@ -36,46 +37,33 @@ public MeshPoint(MeshFace face, Point3d point)
this.W = bary[2];
}
+
///
/// Gets or sets the index of the face this point lies in.
///
- public int FaceIndex
- {
- get;
- set;
- }
+ public int FaceIndex { get; set; }
///
/// Gets or sets the U coordinate at the face.
///
- public double U
- {
- get;
- set;
- }
+ public double U { get; set; }
///
/// Gets or sets the V coordinate at the face.
///
- public double V
- {
- get;
- set;
- }
+ public double V { get; set; }
///
/// Gets or sets the W coordinate at the face.
///
- public double W
- {
- get;
- set;
- }
+ public double W { get; set; }
+
///
/// Converts a mesh point into a string.
///
/// String representation of the mesh point.
- public override string ToString() => "MeshPoint{ " + this.FaceIndex + "; " + this.U + ", " + this.V + ", " + this.W + " }";
+ public override string ToString() =>
+ "MeshPoint{ " + this.FaceIndex + "; " + this.U + ", " + this.V + ", " + this.W + " }";
}
}
\ No newline at end of file
diff --git a/src/Geometry/3D/Mesh/MeshTopology.cs b/src/Geometry/3D/Mesh/MeshTopology.cs
index 3ca6797..b985c3b 100644
--- a/src/Geometry/3D/Mesh/MeshTopology.cs
+++ b/src/Geometry/3D/Mesh/MeshTopology.cs
@@ -11,6 +11,7 @@ public class MeshTopology
// Returns 2 dimensional array: 1 array per vertex index containing an array with the corresponding adjacent member index
private readonly Mesh mesh;
+
///
/// Initializes a new instance of the class.
///
@@ -28,79 +29,58 @@ public MeshTopology(Mesh mesh)
this.EdgeVertex = new Dictionary>();
this.EdgeFace = new Dictionary>();
this.EdgeEdge = new Dictionary>();
+
+ this.ComputeEdgeAdjacency();
+ this.ComputeFaceAdjacency();
+ this.ComputeVertexAdjacency();
}
+
///
/// Gets vertex-Vertex topological connections.
///
- public Dictionary> VertexVertex
- {
- get;
- }
+ public Dictionary> VertexVertex { get; }
///
/// Gets vertex-Face topological connections.
///
- public Dictionary> VertexFaces
- {
- get;
- }
+ public Dictionary> VertexFaces { get; }
///
/// Gets vertex-Edge topological connections.
///
- public Dictionary> VertexEdges
- {
- get;
- }
+ public Dictionary> VertexEdges { get; }
///
/// Gets edge-Edge topological connections.
///
- public Dictionary> EdgeEdge
- {
- get;
- }
+ public Dictionary> EdgeEdge { get; }
///
/// Gets edge-Vertex topological connections.
///
- public Dictionary> EdgeVertex
- {
- get;
- }
+ public Dictionary> EdgeVertex { get; }
///
/// Gets edge-Face topological connections.
///
- public Dictionary> EdgeFace
- {
- get;
- }
+ public Dictionary> EdgeFace { get; }
///
/// Gets face-Vertex topological connections.
///
- public Dictionary> FaceVertex
- {
- get;
- }
+ public Dictionary> FaceVertex { get; }
///
/// Gets face-Edge topological connections.
///
- public Dictionary> FaceEdge
- {
- get;
- }
+ public Dictionary> FaceEdge { get; }
///
/// Gets face-Face topological connections.
///
- public Dictionary> FaceFace
- {
- get;
- }
+ public Dictionary> FaceFace { get; }
+
///
/// Computes vertex adjacency for the whole mesh and stores it in the appropriate dictionaries.
@@ -110,25 +90,32 @@ public void ComputeVertexAdjacency()
foreach (var vertex in this.mesh.Vertices)
{
foreach (var adjacent in vertex.AdjacentVertices())
+ {
if (!this.VertexVertex.ContainsKey(vertex.Index))
this.VertexVertex.Add(vertex.Index, new List {adjacent.Index});
else
this.VertexVertex[vertex.Index].Add(adjacent.Index);
+ }
foreach (var adjacent in vertex.AdjacentFaces())
+ {
if (!this.VertexFaces.ContainsKey(vertex.Index))
this.VertexFaces.Add(vertex.Index, new List {adjacent.Index});
else
this.VertexFaces[vertex.Index].Add(adjacent.Index);
+ }
foreach (var adjacent in vertex.AdjacentEdges())
+ {
if (!this.VertexEdges.ContainsKey(vertex.Index))
this.VertexEdges.Add(vertex.Index, new List {adjacent.Index});
else
this.VertexEdges[vertex.Index].Add(adjacent.Index);
+ }
}
}
+
///
/// Computes face adjacency for the whole mesh and stores it in the appropriate dictionaries.
///
@@ -137,25 +124,32 @@ public void ComputeFaceAdjacency()
foreach (var face in this.mesh.Faces)
{
foreach (var adjacent in face.AdjacentVertices())
+ {
if (!this.FaceVertex.ContainsKey(face.Index))
this.FaceVertex.Add(face.Index, new List {adjacent.Index});
else
this.FaceVertex[face.Index].Add(adjacent.Index);
+ }
foreach (var adjacent in face.AdjacentFaces())
+ {
if (!this.FaceFace.ContainsKey(face.Index))
this.FaceFace.Add(face.Index, new List {adjacent.Index});
else
this.FaceFace[face.Index].Add(adjacent.Index);
+ }
foreach (var adjacent in face.AdjacentEdges())
+ {
if (!this.FaceEdge.ContainsKey(face.Index))
this.FaceEdge.Add(face.Index, new List {adjacent.Index});
else
this.FaceEdge[face.Index].Add(adjacent.Index);
+ }
}
}
+
///
/// Computes edge adjacency for the whole mesh and stores it in the appropriate dictionaries.
///
@@ -164,25 +158,32 @@ public void ComputeEdgeAdjacency()
foreach (var edge in this.mesh.Edges)
{
foreach (var adjacent in edge.AdjacentVertices())
+ {
if (!this.EdgeVertex.ContainsKey(edge.Index))
this.EdgeVertex.Add(edge.Index, new List {adjacent.Index});
else
this.EdgeVertex[edge.Index].Add(adjacent.Index);
+ }
foreach (var adjacent in edge.AdjacentFaces())
+ {
if (!this.EdgeFace.ContainsKey(edge.Index))
this.EdgeFace.Add(edge.Index, new List {adjacent.Index});
else
this.EdgeFace[edge.Index].Add(adjacent.Index);
+ }
foreach (var adjacent in edge.AdjacentEdges())
+ {
if (!this.EdgeEdge.ContainsKey(edge.Index))
this.EdgeEdge.Add(edge.Index, new List {adjacent.Index});
else
this.EdgeEdge[edge.Index].Add(adjacent.Index);
+ }
}
}
+
///
/// Gets the string representation of a given topology dictionary.
///
diff --git a/src/Geometry/3D/Mesh/MeshVertex.cs b/src/Geometry/3D/Mesh/MeshVertex.cs
index d8da16f..0454d04 100644
--- a/src/Geometry/3D/Mesh/MeshVertex.cs
+++ b/src/Geometry/3D/Mesh/MeshVertex.cs
@@ -13,11 +13,13 @@ public class MeshVertex : Point3d
// Constructor
+
///
/// Initializes a new instance of the class.
///
public MeshVertex() => this.userValues = new Dictionary();
+
///
/// Initializes a new instance of the class from a 3D point.
///
@@ -26,8 +28,10 @@ public MeshVertex(Point3d pt)
: base(pt) =>
this.userValues = new Dictionary();
+
///
- /// Initializes a new instance of the class from it's cartesian coordiantes.
+ /// Initializes a new instance of the class from it's cartesian
+ /// coordiantes.
///
/// X Coordiante.
/// Y Coordinate.
@@ -36,23 +40,16 @@ public MeshVertex(double x, double y, double z)
: base(x, y, z) =>
this.userValues = new Dictionary();
+
///
/// Gets or sets the half-edge this vertex is attached to.
///
- public MeshHalfEdge HalfEdge
- {
- get;
- set;
- }
+ public MeshHalfEdge HalfEdge { get; set; }
///
/// Gets or sets the index of the vertex.
///
- public int Index
- {
- get;
- set;
- }
+ public int Index { get; set; }
///
/// Gets dictionary of user values.
@@ -64,24 +61,28 @@ public int Index
// Calculate the valence of a vertex
+
///
/// Computes the valence of the vertex.
///
///
public int Valence() => this.AdjacentHalfEdges().Count;
+
///
/// Check if vertex is isolated, meaning corresponding half-edge is null.
///
///
public bool IsIsolated() => this.HalfEdge == null;
+
///
/// Check if vertex is on mesh boundary.
///
///
public bool OnBoundary() => this.AdjacentHalfEdges().Any(halfEdge => halfEdge.OnBoundary);
+
///
/// Returns a list with all adjacent HE_HalfEdge of this vertex.
///
@@ -99,6 +100,7 @@ public List AdjacentHalfEdges()
return halfEdges;
}
+
///
/// Returns a list with all adjacent HE_Face of a vertex.
///
@@ -117,6 +119,7 @@ public List AdjacentFaces()
return faces;
}
+
///
/// Returns a list with all the adjacent HE_Vertex of this vertex.
///
@@ -134,6 +137,7 @@ public List AdjacentVertices()
return vertices;
}
+
///
/// Returns a list with all the adjacent HE_Edge of this vertex.
///
@@ -151,6 +155,7 @@ public List AdjacentEdges()
return edges;
}
+
///
/// Returns a list with all the adjacent HE_Corners of this vertex.
///
@@ -169,6 +174,7 @@ public List AdjacentCorners()
return corners;
}
+
///
/// Returns the string representation of this vertex.
///
diff --git a/src/Geometry/3D/Nurbs/NurbsCalculator.cs b/src/Geometry/3D/Nurbs/NurbsCalculator.cs
deleted file mode 100644
index ae84c43..0000000
--- a/src/Geometry/3D/Nurbs/NurbsCalculator.cs
+++ /dev/null
@@ -1,750 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Paramdigma.Core.Collections;
-
-namespace Paramdigma.Core.Geometry
-{
- ///
- /// Contains all methods related to 'The Nurbs Book 2nd Edition' implementation of NURBS curves and surfaces.
- ///
- public static class NurbsCalculator
- {
- ///
- /// Constructs a Unit knot vector given a point count and degree.
- ///
- /// Ammount of control points in the curve.
- /// Degree of the curve.
- ///
- public static double[] CreateUnitKnotVector(int controlPointCount, int degree)
- {
- if (degree > controlPointCount)
- throw new Exception("Degree cannot be bigger than 'ControlPoints - 1'");
- var knotVector = new double[controlPointCount + degree + 2];
- for (var i = 0; i <= degree; i++)
- knotVector[i] = 0.0;
- for (var i = degree + 1; i < controlPointCount + 1; i++)
- knotVector[i] = ((double)i - degree) / ((controlPointCount - degree) + 1);
- for (var i = controlPointCount + 1; i < controlPointCount + degree + 2; i++)
- knotVector[i] = 1.0;
- return knotVector;
- }
-
- ///
- /// Compute a point on a power basis curve.
- ///
- /// Curve points.
- /// Curve degree.
- /// Parameter.
- /// Computed point on curve.
- public static Point3d Horner1(Point3d[] points, int degree, double t)
- {
- var c = points[degree];
- for (var i = degree - 1; i >= 0; i--)
- c = (c * t) + points[i];
- return c;
- }
-
- ///
- /// Compute the value of a Bernstein polynomial.
- ///
- /// Index of point to compute polynomial of.
- /// Degree of curve.
- /// Parameter.
- ///
- public static double Bernstein(int index, int degree, double t)
- {
- var temp = new List();
- for (var j = 0; j <= degree; j++)
- temp.Add(0.0);
-
- temp[degree - index] = 1.0;
-
- var u1 = 1.0 - t;
- for (var k = 1; k <= degree; k++)
- for (var j = degree; j >= k; j--)
- temp[j] = (u1 * temp[j]) + (t * temp[j - 1]);
-
- return temp[degree];
- }
-
- ///
- /// Compute point on a power basis surface.
- ///
- /// Control point matrix.
- /// Surface degree in the U direction.
- /// Surface degree in the V direction.
- /// U parameter to compute.
- /// V parameter to compute.
- /// Computed point on the surface.
- public static Point3d Horner2(Matrix controlPoints, int degreeU, int degreeV, double u, double v)
- {
- var b = new Point3d[degreeU];
- for (var i = 0; i <= degreeU; i++)
- b[i] = Horner1(controlPoints.Row(i), degreeV, v);
- return Horner1(b, degreeU, u);
- }
-
- ///
- /// Compute all nth-degree Bernstein polynomials.
- ///
- /// Curve degree.
- /// Parameter.
- ///
- public static double[] AllBernstein(int degree, double t)
- {
- var b = new double[degree + 1];
- b[0] = 1.0;
- var u1 = 1.0 - t;
- for (var j = 1; j <= degree; j++)
- {
- var saved = 0.0;
- for (var k = 0; k < j; k++)
- {
- var temp = b[k];
- b[k] = saved + (u1 * temp);
- saved = t * temp;
- }
-
- b[j] = saved;
- }
-
- return b;
- }
-
- ///
- /// Compute point on Bezier curve.
- ///
- /// Control ponts of the curve.
- /// Curve degree.
- /// Parameter to compute.
- ///
- public static Point3d PointOnBezierCurve(List controlPoints, int degree, double t)
- {
- var c = new Point3d();
-
- var b = AllBernstein(degree, t);
- for (var k = 0; k <= degree; k++)
- c += b[k] * controlPoints[k];
-
- return c;
- }
-
- ///
- /// Compute point on a Bézier curve by deCasteljau.
- ///
- /// Control points of the curve.
- /// Curve degree.
- /// Parameter of point to compute.
- /// Computed point along the Bézier curve.
- public static Point3d DeCasteljau1(Point3d[] controlPoints, int degree, double t)
- {
- var q = new Point3d[degree + 1];
- for (var i = 0; i <= degree; i++)
- q[i] = new Point3d(controlPoints[i]);
-
- for (var k = 1; k <= degree; k++)
- for (var i = 0; i <= degree - k; i++)
- q[i] = ((1.0 - t) * q[i]) + (t * q[i + 1]);
-
- return q[0];
- }
-
- ///
- /// Compute a point on a Bézier surface by deCasteljau.
- ///
- /// Control points of the curve.
- /// Surface degree in the U direction.
- /// Surface degree in the V direction.
- /// U parameter to compute.
- /// V parameter to compute.
- /// The computed point.
- public static Point3d DeCasteljau2(Matrix controlPoints, int degreeU, int degreeV, double u, double v)
- {
- var q = new List();
- if (degreeU <= degreeV)
- {
- for (var j = 0; j <= degreeV; j++)
- q.Add(DeCasteljau1(controlPoints.Row(j), degreeU, u));
-
- return DeCasteljau1(q.ToArray(), degreeV, v);
- }
-
- for (var i = 0; i <= degreeU; i++)
- q.Add(DeCasteljau1(controlPoints.Column(i), degreeU, u));
- return DeCasteljau1(q.ToArray(), degreeU, u);
- }
-
- ///
- /// Determine the knot span index.
- ///
- /// Degree.
- /// ????.
- /// Paramter.
- /// Knot vector.
- /// The knot span index.
- public static int FindSpan(int n, int degree, double t, IList knotVector)
- {
- if (t == knotVector[n + 1])
- return n;
-
- var low = degree;
- var high = n + 1;
- var mid = (low + high) / 2;
- while (t < knotVector[mid] || t >= knotVector[mid + 1])
- {
- if (t < knotVector[mid])
- high = mid;
- else
- low = mid;
-
- mid = (low + high) / 2;
- }
-
- return mid;
- }
-
- ///
- /// Compute all non-zero basis functions of all degrees from 0 to "degree".
- ///
- /// Knot span index.
- /// Parameter to compute.
- /// Degree.
- /// The knot vector.
- /// List with all non-zero basis functions up to the specified degree.
- public static double[,] AllBasisFuns(int span, double param, int degree, IList knotVector)
- {
- var n = new double[degree + 1, degree + 1];
- for (var i = 0; i <= degree; i++)
- for (var j = 0; j <= i; j++)
- n[j, i] = OneBasisFun(degree, knotVector.Count - 1, knotVector, (span - i) + j, param);
-
- return n;
- }
-
- ///
- /// Computes the basis functions of a span.
- ///
- /// Knot span.
- /// Parameter to compute.
- /// Degree.
- /// Knot vector.
- /// List of the basis functions of the specific span.
- public static double[] BasisFuns(int span, double param, int degree, IList knotVector)
- {
- var basisFunctions = new double[degree + 1];
- var left = new double[degree + 1];
- var right = new double[degree + 1];
- basisFunctions[0] = 1.0;
- for (var j = 1; j <= degree; j++)
- {
- left[j] = param - knotVector[(span + 1) - j];
- right[j] = knotVector[span + j] - param;
- var saved = 0.0;
- for (var r = 0; r < j; r++)
- {
- var temp = basisFunctions[r] / (right[r + 1] + left[j - r]);
- basisFunctions[r] = saved + (right[r + 1] * temp);
- saved = left[j - r] * temp;
- }
-
- basisFunctions[j] = saved;
- }
-
- return basisFunctions;
- }
-
- ///
- /// Compute nonzero basis functions and their derivatives at a specified parameter.
- ///
- /// Knot span.
- /// Parameter.
- /// Degree.
- /// Derivatives to compute.
- /// Knot vector.
- /// Multidimensional array holding the basis functions and their derivatives for that parameter.
- public static Matrix DersBasisFuns(int span, double param, int degree, int n, IList knotVector)
- {
- var ders = new Matrix(n, degree);
- var ndu = new double[degree + 1, degree + 1];
- var a = new double[2, degree + 1];
- var left = new double[degree + 1];
- var right = new double[degree + 1];
-
- ndu[0, 0] = 1.0;
- for (var j = 1; j <= degree; j++)
- {
- left[j] = param - knotVector[(span + 1) - j];
- right[j] = knotVector[span + j] - param;
- var saved = 0.0;
- for (var r = 0; r < j; r++)
- {
- ndu[j, r] = right[r + 1] + left[j - r];
- var temp = ndu[r, j - 1] / ndu[j, r];
- ndu[r, j] = saved + (right[r + 1] * temp);
- saved = left[j - r] * temp;
- }
-
- ndu[j, j] = saved;
- }
-
- for (var j = 0; j <= degree; j++)
- ders[0, j] = ndu[j, degree];
-
- for (var r = 0; r <= degree; r++)
- {
- var s1 = 0;
- var s2 = 1;
- a[0, 0] = 1.0;
- for (var k = 1; k <= n; k++)
- {
- var d = 0.0;
- var rk = r - k;
- var pk = degree - k;
- if (r >= k)
- {
- a[s2, 0] = a[s1, 0] / ndu[pk + 1, rk];
- d = a[s2, 0] * ndu[rk, pk];
- }
-
- var j1 = rk >= -1 ? 1 : -rk;
- var j2 = r - 1 <= pk ? k - 1 : degree - r;
-
- for (var j = j1; j <= j2; j++)
- {
- a[s2, j] = (a[s1, j] - a[s1, j - 1]) / ndu[pk + 1, rk + j];
- d += a[s2, j] * ndu[rk + j, pk];
- }
-
- if (r <= pk)
- {
- a[s2, k] = -a[s1, k - 1] / ndu[pk + 1, r];
- d += a[s2, k] * ndu[r, pk];
- }
-
- ders[k, r] = d;
-
- // Switch rows
- var temp = s1;
- s1 = s2;
- s2 = temp;
- }
- }
-
- var r0 = degree;
- for (var k = 1; k <= n; k++)
- {
- for (var j = 0; j <= degree; j++)
- ders[k, j] *= r0;
- r0 *= degree - k;
- }
-
- return ders;
- }
-
- ///
- /// Compute the basis function 'Nip'.
- ///
- /// Degree.
- /// The high index of the knot vector.
- /// Knot vector.
- /// Knot span index.
- /// Parameter to compute.
- ///
- public static double OneBasisFun(int degree, int m, IList knotVector, int span, double param)
- {
- if ((span == 0 && param == knotVector[0]) || (span == m - degree - 1 && param == knotVector[m]))
- return 1.0;
-
- if (param < knotVector[span] || param >= knotVector[span + degree + 1])
- return 0.0;
-
- // Initialize zeroth-degree functions
- var n = new double[degree + 1];
- for (var j = 0; j <= degree; j++)
- if (param >= knotVector[span + j] && param < knotVector[span + j + 1])
- n[j] = 1.0;
- else
- n[j] = 0.0;
-
- for (var k = 1; k <= degree; k++)
- {
- var saved = n[0] == 0.0 ? 0.0 : ((param - knotVector[span]) * n[0]) / (knotVector[span + k] - knotVector[span]);
- for (var j = 0; j < (degree - k) + 1; j++)
- {
- var uLeft = knotVector[span + j + 1];
- var uRight = knotVector[span + j + k + 1];
- if (n[j + 1] == 0.0)
- {
- n[j] = saved;
- saved = 0.0;
- }
- else
- {
- var temp = n[j + 1] / (uRight - uLeft);
- n[j] = saved + ((uRight - param) * temp);
- saved = (param - uLeft) * temp;
- }
- }
- }
-
- return n[0];
- }
-
- ///
- /// Compute derivatives of basis function 'Nip'.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public static double[] DersOneBasisFun(int p, int m, IList knotVector, int i, double u, int n)
- {
- // TODO: Check unused m parameter.
- var ders = new double[n + 1];
- if (u < knotVector[i] || u >= knotVector[i + p + 1])
- {
- for (var k = 0; k <= n; k++)
- ders[k] = 0.0;
- return ders;
- }
-
- var tmpN = new double[p + 1, p + 1];
- for (var j = 0; j <= p; j++)
- if (u >= knotVector[i + j] && u < knotVector[i + j + 1])
- tmpN[j, 0] = 1.0;
-
- for (var k = 1; k <= p; k++)
- {
- double saved;
- if (tmpN[0, k - 1] == 0.0)
- saved = 0.0;
- else
- saved = ((u - knotVector[i]) * tmpN[0, k - 1]) / (knotVector[i + k] - knotVector[i]);
-
- for (var j = 0; j < (p - k) + 1; j++)
- {
- var uLeft = knotVector[i + j + 1];
- var uRight = knotVector[i + j + k + 1];
- if (tmpN[j + 1, k - 1] == 0.0)
- {
- tmpN[j, k] = saved;
- saved = 0.0;
- }
- else
- {
- var temp = tmpN[j + 1, k - 1] / (uRight - uLeft);
- tmpN[j, k] = saved + ((uRight - u) * temp);
- saved = (u - uLeft) * temp;
- }
- }
- }
-
- ders[0] = tmpN[0, p];
-
- var tempND = new double[n + 1];
- for (var k = 1; k <= n; k++)
- {
- for (var j = 0; j <= k; j++)
- tempND[j] = tmpN[j, p - k];
-
- for (var jj = 1; jj <= k; jj++)
- {
- double saved;
- if (tempND[0] == 0.0)
- saved = 0.0;
- else
- saved = tempND[0] / (knotVector[((i + p) - k) + jj] - knotVector[i]);
- for (var j = 0; j < (k - jj) + 1; j++)
- {
- var uLeft = knotVector[i + j + 1];
- var uRight = knotVector[i + j + p + jj + 1];
- if (tempND[j + 1] == 0.0)
- {
- tempND[j] = ((p - k) + jj) * saved;
- saved = 0.0;
- }
- else
- {
- var temp = tempND[j + 1] / (uRight - uLeft);
- tempND[j] = ((p - k) + jj) * (saved - temp);
- saved = temp;
- }
- }
- }
-
- ders[k] = tempND[0];
- }
-
- return ders;
- }
-
- public static Point3d CurvePoint(int n, int p, IList knotVector, IList controlPoints, double u)
- {
- var span = FindSpan(n, p, u, knotVector);
- var basisFuns = BasisFuns(span, u, p, knotVector);
- var c = Point3d.Unset;
- for (var i = 0; i <= p; i++)
- c += basisFuns[i] * controlPoints[(span - p) + i];
- return c;
- }
-
- public static Vector3d[] CurveDerivsAlg1(int n, int p, IList knotVector, IList controlPoints, double u, int d)
- {
- var ck = new Vector3d[d + 1];
- var du = Math.Min(d, p);
- for (var k = p + 1; k <= d; k++)
- ck[k] = new Vector3d();
- var span = FindSpan(n, p, u, knotVector);
- var nders = DersBasisFuns(span, u, p, du, knotVector);
- for (var k = 0; k <= du; k++)
- {
- ck[k] = new Vector3d();
- for (var j = 0; j <= p; j++)
- ck[k] += nders[k, j] * (Vector3d)controlPoints[(span - p) + j];
- }
-
- return ck;
- }
-
- public static Point3d[,] CurveDerivCpts(int n, int p, IList knotVector, IList controlPoints, int d, int r1, int r2)
- {
- var r = r2 - r1;
- var pk = new Point3d[d + 1, r];
- for (var i = 0; i <= r; i++)
- pk[0, i] = controlPoints[r1 + i];
- for (var k = 1; k <= d; k++)
- {
- var tmp = (p - k) + 1;
- for (var i = 0; i <= r - k; i++)
- pk[k, i] = (tmp * (Point3d)(pk[k - 1, i + 1] - pk[k - 1, i])) / (knotVector[r1 + i + p + 1] - knotVector[r1 + i + k]);
- }
-
- return pk;
- }
-
- public static Vector3d[] CurveDerivsAlg2(int n, int p, IList knotVector, IList controlPoints, double u, int d)
- {
- var du = Math.Min(d, p);
- var ck = new Vector3d[d + 1];
- for (var k = p + 1; k <= d; k++)
- ck[k] = new Vector3d();
- var span = FindSpan(n, p, u, knotVector);
- var basisFuns = AllBasisFuns(span, u, p, knotVector);
- var pk = CurveDerivCpts(n, p, knotVector, controlPoints, du, span - p, span);
- for (var k = 0; k <= du; k++)
- {
- ck[k] = new Vector3d();
- for (var j = 0; j <= p - k; j++)
- ck[k] += basisFuns[j, p - k] * (Vector3d)pk[k, j];
- }
-
- return ck;
- }
-
- // B-Spline Surfaces
- public static Point3d SurfacePoint(int n, int p, IList knotVectorU, int m, int q, IList knotVectorV, Matrix controlPoints, double u, double v)
- {
- var uspan = FindSpan(n, p, u, knotVectorU);
- var nU = BasisFuns(uspan, u, p, knotVectorU);
- var vspan = FindSpan(m, q, v, knotVectorV);
- var nV = BasisFuns(vspan, v, q, knotVectorV);
- var uind = uspan - p;
- var surfPt = Point3d.Unset;
- for (var l = 0; l <= q; l++)
- {
- var temp = Point3d.Unset;
- var vind = vspan - q - l;
- for (var k = 0; k <= p; k++)
- temp += nU[k] * controlPoints[uind + k, vind];
- surfPt += nV[l] * temp;
- }
-
- return surfPt;
- }
-
- public static Point3d[,] SurfaceDerivsAlg1(int n, int p, IList knotVectorU, int m, int q, IList knotVectorV, Matrix controlPoints, double u, double v, int derivCount)
- {
- var sKL = new Point3d[derivCount + 1, derivCount + 1];
- var du = Math.Min(derivCount, p);
- for (var k = p + 1; k <= derivCount; k++)
- for (var l = 0; l <= derivCount - k; l++)
- sKL[k, l] = Point3d.Unset;
-
- var dv = Math.Min(derivCount, q);
- for (var l = q + 1; l <= derivCount; l++)
- for (var k = 0; k <= derivCount - 1; k++)
- sKL[k, l] = Point3d.Unset;
-
- var uSpan = FindSpan(n, p, u, knotVectorU);
- var nU = DersBasisFuns(uSpan, u, p, du, knotVectorU);
- var vSpan = FindSpan(m, q, v, knotVectorV);
- var nV = DersBasisFuns(vSpan, v, q, dv, knotVectorV);
-
- for (var k = 0; k <= du; k++)
- {
- var temp = new Point3d[q];
- for (var s = 0; s <= q; s++)
- {
- temp[s] = Point3d.Unset;
- for (var r = 0; r <= p; r++)
- temp[s] += nU[k, r] * controlPoints[(uSpan - p) + r, (vSpan - q) + s];
-
- var dd = Math.Min(derivCount - k, dv);
-
- for (var l = 0; l <= dd; l++)
- {
- sKL[k, l] = Point3d.Unset;
-
- // TODO: Check ss, this was changed from 's' for naming conflicts but it might have been on purpose.
- for (var ss = 0; ss <= q; ss++)
- sKL[k, l] += nV[l, ss] * temp[ss];
- }
- }
- }
-
- return sKL;
- }
-
- public static Point3d[][][][] SurfaceDerivCpts(int n, int p, IList knotVectorU, int m, int q, IList knotVectorV, Matrix controlPoints, int d, int r1, int r2, int s1, int s2)
- {
- var pkl = new Point3d[d][][][];
-
- var du = Math.Min(d, p);
- var dv = Math.Min(d, q);
- var r = r2 - r1;
- var s = s2 - s1;
-
- for (var j = s1; j <= s2; j++)
- {
- var temp = CurveDerivCpts(n, p, knotVectorU, controlPoints.Column(j), du, r1, r2);
- for (var k = 0; k <= du; k++)
- for (var i = 0; i <= r - k; i++)
- pkl[k][0][i][j - s1] = temp[k, i];
- }
-
- for (var k = 0; k <= du; k++)
- for (var i = 0; i <= r - k; i++)
- {
- var dd = Math.Min(d - k, dv);
- var temp = CurveDerivCpts(m, q, knotVectorV, pkl[k][0][i], dd, 0, s);
- for (var l = i; k <= dd; k++)
- for (var j = 0; j <= s - l; j++)
- pkl[k][l][i][j] = temp[l, j];
- }
-
- return pkl;
- }
-
- public static Point3d[,] SurfaceDerivsAlg2(int n, int p, IList knotVectorU, int m, int q, IList knotVectorV, Matrix controlPoints, double u, double v, int d)
- {
- var skl = new Point3d[d + 1, d + 1];
-
- var du = Math.Min(d, p);
- for (var k = p + 1; k <= d; k++)
- for (var l = 0; l <= d - k; l++)
- skl[k, l] = Point3d.Unset;
-
- var dv = Math.Min(d, q);
- for (var l = q + 1; l <= d; l++)
- for (var k = 0; k <= d - l; k++)
- skl[k, l] = Point3d.Unset;
-
- var uSpan = FindSpan(n, p, u, knotVectorU);
- var nV = AllBasisFuns(uSpan, u, p, knotVectorU);
- var vSpan = FindSpan(m, q, v, knotVectorV);
- var nU = AllBasisFuns(vSpan, v, q, knotVectorV);
- var pkl = SurfaceDerivCpts(n, p, knotVectorU, m, q, knotVectorV, controlPoints, d, uSpan - p, uSpan, vSpan - q, vSpan);
-
- for (var k = 0; k <= du; k++)
- {
- var dd = Math.Min(d - k, dv);
- for (var l = 0; l <= dd; l++)
- {
- skl[k, l] = Point3d.Unset;
- for (var i = 0; i <= q - l; i++)
- {
- var tmp = Point3d.Unset;
- for (var j = 0; j <= p - k; j++)
- tmp += nV[j, p - k] * pkl[k][l][j][i];
- skl[k, l] += nU[i, q - l] * tmp;
- }
- }
- }
-
- return skl;
- }
-
- // Nubs methods
- public static Point4d CurvePoint(int n, int p, IList knotVector, IList controlPoints, double u)
- {
- var span = FindSpan(n, p, u, knotVector);
- var N = BasisFuns(span, u, p, knotVector);
- var cw = new Point4d();
- for (var j = 0; j <= p; j++)
- cw += N[j] * controlPoints[(span - p) + j];
-
- return cw / cw.Weight;
- }
-
- ///
- /// Compute C(u) derivatives from Cw(u) derivatives.
- ///
- ///
- ///
- ///
- ///
- public static Vector3d[] RatCurveDerivs(IList aDers, IList wDers, int d)
- {
- var ders = new Vector3d[d + 1];
- double[,] bin = null; // TODO: Precompute 'binomial coefficients'
- for (var k = 0; k <= d; k++)
- {
- var v = aDers[k];
- for (var i = 1; i <= k; i++)
- v -= bin[k, i] * wDers[i] * ders[k - i];
-
- ders[k] = v / wDers[0];
- }
-
- return ders;
- }
-
- ///
- /// Compute a point on a nurbs surface.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public static Point3d SurfacePoint(int n, int p, IList knotVectorU, int m, int q, IList knotVectorV, Matrix controlPoints, double u, double v)
- {
- var uspan = FindSpan(n, p, u, knotVectorU);
- var nU = BasisFuns(uspan, u, p, knotVectorU);
- var vspan = FindSpan(m, q, v, knotVectorV);
- var nV = BasisFuns(vspan, v, q, knotVectorV);
-
- var temp = new Point4d[q];
- for (var l = 0; l <= q; l++)
- {
- temp[l] = new Point4d();
- for (var k = 0; k <= p; k++)
- temp[l] += nU[k] * controlPoints[(uspan - p) + k, (vspan - q) + l];
- }
-
- var sW = new Point4d();
- for (var l = 0; l <= q; l++)
- sW += nV[l] * temp[l];
-
- return (Point3d)(sW / sW.Weight);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Geometry/3D/Nurbs/NurbsSurface.cs b/src/Geometry/3D/Nurbs/NurbsSurface.cs
deleted file mode 100644
index d2e551f..0000000
--- a/src/Geometry/3D/Nurbs/NurbsSurface.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Paramdigma.Core.Geometry
-{
- ///
- /// Represents a NURBS surface. Contains properties and methods for operating with NURBS surfaces.
- ///
- public class NurbsSurface { }
-}
\ No newline at end of file
diff --git a/src/Geometry/3D/NurbsCalculator.cs b/src/Geometry/3D/NurbsCalculator.cs
new file mode 100644
index 0000000..c0def04
--- /dev/null
+++ b/src/Geometry/3D/NurbsCalculator.cs
@@ -0,0 +1,1350 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Paramdigma.Core.Collections;
+
+namespace Paramdigma.Core.Geometry
+{
+ ///
+ /// Contains all methods related to 'The Nurbs Book 2nd Edition' implementation of NURBS curves and
+ /// surfaces.
+ ///
+ internal static class NurbsCalculator
+ {
+ ///
+ /// Constructs a Unit knot vector given a point count and degree.
+ ///
+ /// Ammount of control points in the curve.
+ /// Degree of the curve.
+ /// Throws an error if degree is bigger than controlPoints+1
+ ///
+ public static double[] CreateUniformKnotVector(int controlPointCount, int degree)
+ {
+ if (degree > controlPointCount)
+ throw new Exception("Degree cannot be bigger than 'ControlPoints - 1'");
+ var knotVector = new double[controlPointCount + degree + 1];
+ for (var i = 0; i <= degree; i++)
+ knotVector[i] = 0.0;
+ for (var i = degree + 1; i < controlPointCount; i++)
+ knotVector[i] = (( double ) i - degree) / (controlPointCount - degree);
+ for (var i = controlPointCount; i < controlPointCount + degree + 1; i++)
+ knotVector[i] = 1.0;
+ return knotVector;
+ }
+
+
+ ///
+ /// Compute a point on a power basis curve.
+ ///
+ /// Curve points.
+ /// Curve degree.
+ /// Parameter.
+ /// Computed point on curve.
+ public static Point3d Horner1(Point3d[] points, int degree, double t)
+ {
+ var c = points[degree];
+ for (var i = degree - 1; i >= 0; i--)
+ c = c * t + points[i];
+ return c;
+ }
+
+
+ ///
+ /// Compute the value of a Bernstein polynomial.
+ ///
+ /// Index of point to compute polynomial of.
+ /// Degree of curve.
+ /// Parameter.
+ ///
+ public static double Bernstein(int index, int degree, double t)
+ {
+ var temp = new List();
+ for (var j = 0; j <= degree; j++)
+ temp.Add(0.0);
+
+ temp[degree - index] = 1.0;
+
+ var u1 = 1.0 - t;
+ for (var k = 1; k <= degree; k++)
+ {
+ for (var j = degree; j >= k; j--)
+ temp[j] = u1 * temp[j] + t * temp[j - 1];
+ }
+
+ return temp[degree];
+ }
+
+
+ ///
+ /// Compute point on a power basis surface.
+ ///
+ /// Control point matrix.
+ /// Surface degree in the U direction.
+ /// Surface degree in the V direction.
+ /// U parameter to compute.
+ /// V parameter to compute.
+ /// Computed point on the surface.
+ public static Point3d Horner2(
+ Matrix controlPoints,
+ int degreeU,
+ int degreeV,
+ double u,
+ double v)
+ {
+ var b = new Point3d[degreeU];
+ for (var i = 0; i <= degreeU; i++)
+ b[i] = Horner1(controlPoints.Row(i), degreeV, v);
+ return Horner1(b, degreeU, u);
+ }
+
+
+ ///
+ /// Compute all nth-degree Bernstein polynomials.
+ ///
+ /// Curve degree.
+ /// Parameter.
+ ///
+ public static double[] AllBernstein(int degree, double t)
+ {
+ var b = new double[degree + 1];
+ b[0] = 1.0;
+ var u1 = 1.0 - t;
+ for (var j = 1; j <= degree; j++)
+ {
+ var saved = 0.0;
+ for (var k = 0; k < j; k++)
+ {
+ var temp = b[k];
+ b[k] = saved + u1 * temp;
+ saved = t * temp;
+ }
+
+ b[j] = saved;
+ }
+
+ return b;
+ }
+
+
+ ///
+ /// Compute point on Bezier curve.
+ ///
+ /// Control ponts of the curve.
+ /// Curve degree.
+ /// Parameter to compute.
+ ///
+ public static Point3d PointOnBezierCurve(List controlPoints, int degree, double t)
+ {
+ var c = new Point3d();
+
+ var b = AllBernstein(degree, t);
+ for (var k = 0; k <= degree; k++)
+ c += b[k] * controlPoints[k];
+
+ return c;
+ }
+
+
+ ///
+ /// Compute point on a Bézier curve by deCasteljau.
+ ///
+ /// Control points of the curve.
+ /// Curve degree.
+ /// Parameter of point to compute.
+ /// Computed point along the Bézier curve.
+ public static Point3d DeCasteljau1(Point3d[] controlPoints, int degree, double t)
+ {
+ var q = new Point3d[degree + 1];
+ for (var i = 0; i <= degree; i++)
+ q[i] = new Point3d(controlPoints[i]);
+
+ for (var k = 1; k <= degree; k++)
+ {
+ for (var i = 0; i <= degree - k; i++)
+ q[i] = (1.0 - t) * q[i] + t * q[i + 1];
+ }
+
+ return q[0];
+ }
+
+
+ ///
+ /// Compute a point on a Bézier surface by deCasteljau.
+ ///
+ /// Control points of the curve.
+ /// Surface degree in the U direction.
+ /// Surface degree in the V direction.
+ /// U parameter to compute.
+ /// V parameter to compute.
+ /// The computed point.
+ public static Point3d DeCasteljau2(
+ Matrix controlPoints,
+ int degreeU,
+ int degreeV,
+ double u,
+ double v)
+ {
+ var q = new List();
+ if (degreeU <= degreeV)
+ {
+ for (var j = 0; j <= degreeV; j++)
+ q.Add(DeCasteljau1(controlPoints.Row(j), degreeU, u));
+
+ return DeCasteljau1(q.ToArray(), degreeV, v);
+ }
+
+ for (var i = 0; i <= degreeU; i++)
+ q.Add(DeCasteljau1(controlPoints.Column(i), degreeU, u));
+ return DeCasteljau1(q.ToArray(), degreeU, u);
+ }
+
+
+ ///
+ /// Determine the knot span index.
+ ///
+ /// Degree.
+ /// ????.
+ /// Parameter.
+ /// Knot vector.
+ /// The knot span index.
+ public static int FindSpan(int n, int degree, double t, IList knotVector)
+ {
+ if (t >= knotVector[n + 1])
+ return n;
+
+ var low = degree;
+ var high = n + 1;
+ var mid = (low + high) / 2;
+ while (t < knotVector[mid] || t >= knotVector[mid + 1])
+ {
+ if (t < knotVector[mid])
+ high = mid;
+ else
+ low = mid;
+
+ mid = (low + high) / 2;
+ }
+
+ return mid;
+ }
+
+
+ ///
+ /// Compute all non-zero basis functions of all degrees from 0 to "degree".
+ ///
+ /// Knot span index.
+ /// Parameter to compute.
+ /// Degree.
+ /// The knot vector.
+ /// List with all non-zero basis functions up to the specified degree.
+ public static double[,] AllBasisFuns(
+ int span,
+ double param,
+ int degree,
+ IList knotVector)
+ {
+ var n = new double[degree + 1, degree + 1];
+ for (var i = 0; i <= degree; i++)
+ {
+ for (var j = 0; j <= i; j++)
+ {
+ n[j, i] = OneBasisFun(
+ degree,
+ knotVector.Count - 1,
+ knotVector,
+ span - i + j,
+ param);
+ }
+ }
+
+ return n;
+ }
+
+
+ ///
+ /// Computes the basis functions of a span.
+ ///
+ /// Knot span.
+ /// Parameter to compute.
+ /// Degree.
+ /// Knot vector.
+ /// List of the basis functions of the specific span.
+ public static double[] BasisFunctions(
+ int span,
+ double param,
+ int degree,
+ IList knotVector)
+ {
+ // INFO: This method is called 'BasisFuns' in the book.
+ var basisFunctions = new double[degree + 1];
+ var left = new double[degree + 1];
+ var right = new double[degree + 1];
+ basisFunctions[0] = 1.0;
+ for (var j = 1; j <= degree; j++)
+ {
+ left[j] = param - knotVector[span + 1 - j];
+ right[j] = knotVector[span + j] - param;
+ var saved = 0.0;
+ for (var r = 0; r < j; r++)
+ {
+ var temp = basisFunctions[r] / (right[r + 1] + left[j - r]);
+ basisFunctions[r] = saved + right[r + 1] * temp;
+ saved = left[j - r] * temp;
+ }
+
+ basisFunctions[j] = saved;
+ }
+
+ return basisFunctions;
+ }
+
+
+ ///
+ /// Compute nonzero basis functions and their derivatives at a specified parameter.
+ ///
+ /// Knot span.
+ /// Parameter.
+ /// Degree.
+ /// Derivatives to compute.
+ /// Knot vector.
+ ///
+ /// Multidimensional array holding the basis functions and their derivatives for that
+ /// parameter.
+ ///
+ public static Matrix DerivativeBasisFunctions(
+ int span,
+ double param,
+ int degree,
+ int n,
+ IList knotVector)
+ {
+ // INFO: This method is called 'DersBasisFuns' in the book.
+ var ders = new Matrix(n + 1, degree + 1);
+ var ndu = new double[degree + 1, degree + 1];
+ var a = new double[2, degree + 1];
+ var left = new double[degree + 1];
+ var right = new double[degree + 1];
+
+ ndu[0, 0] = 1.0;
+ for (var j = 1; j <= degree; j++)
+ {
+ left[j] = param - knotVector[span + 1 - j];
+ right[j] = knotVector[span + j] - param;
+ var saved = 0.0;
+ for (var r = 0; r < j; r++)
+ {
+ ndu[j, r] = right[r + 1] + left[j - r];
+ var temp = ndu[r, j - 1] / ndu[j, r];
+ ndu[r, j] = saved + right[r + 1] * temp;
+ saved = left[j - r] * temp;
+ }
+
+ ndu[j, j] = saved;
+ }
+
+ for (var j = 0; j <= degree; j++)
+ ders[0, j] = ndu[j, degree];
+
+ for (var r = 0; r <= degree; r++)
+ {
+ var s1 = 0;
+ var s2 = 1;
+ a[0, 0] = 1.0;
+ for (var k = 1; k <= n; k++)
+ {
+ var d = 0.0;
+ var rk = r - k;
+ var pk = degree - k;
+ if (r >= k)
+ {
+ a[s2, 0] = a[s1, 0] / ndu[pk + 1, rk];
+ d = a[s2, 0] * ndu[rk, pk];
+ }
+
+ var j1 = rk >= -1 ? 1 : -rk;
+ var j2 = r - 1 <= pk ? k - 1 : degree - r;
+
+ for (var j = j1; j <= j2; j++)
+ {
+ a[s2, j] = (a[s1, j] - a[s1, j - 1]) / ndu[pk + 1, rk + j];
+ d += a[s2, j] * ndu[rk + j, pk];
+ }
+
+ if (r <= pk)
+ {
+ a[s2, k] = -a[s1, k - 1] / ndu[pk + 1, r];
+ d += a[s2, k] * ndu[r, pk];
+ }
+
+ ders[k, r] = d;
+
+ // Switch rows
+ var temp = s1;
+ s1 = s2;
+ s2 = temp;
+ }
+ }
+
+ var r0 = degree;
+ for (var k = 1; k <= n; k++)
+ {
+ for (var j = 0; j <= degree; j++)
+ ders[k, j] *= r0;
+ r0 *= degree - k;
+ }
+
+ return ders;
+ }
+
+
+ ///
+ /// Compute the basis function 'Nip'.
+ ///
+ /// Degree.
+ /// The high index of the knot vector.
+ /// Knot vector.
+ /// Knot span index.
+ /// Parameter to compute.
+ ///
+ public static double OneBasisFun(
+ int degree,
+ int m,
+ IList knotVector,
+ int span,
+ double param)
+ {
+ if (span == 0 && param == knotVector[0]
+ || span == m - degree - 1 && param == knotVector[m])
+ return 1.0;
+
+ if (param < knotVector[span] || param >= knotVector[span + degree + 1])
+ return 0.0;
+
+ // Initialize zeroth-degree functions
+ var n = new double[degree + 1];
+ for (var j = 0; j <= degree; j++)
+ {
+ if (param >= knotVector[span + j] && param < knotVector[span + j + 1])
+ n[j] = 1.0;
+ else
+ n[j] = 0.0;
+ }
+
+ for (var k = 1; k <= degree; k++)
+ {
+ var saved = n[0] == 0.0
+ ? 0.0
+ : (param - knotVector[span]) * n[0]
+ / (knotVector[span + k] - knotVector[span]);
+ for (var j = 0; j < degree - k + 1; j++)
+ {
+ var uLeft = knotVector[span + j + 1];
+ var uRight = knotVector[span + j + k + 1];
+ if (n[j + 1] == 0.0)
+ {
+ n[j] = saved;
+ saved = 0.0;
+ }
+ else
+ {
+ var temp = n[j + 1] / (uRight - uLeft);
+ n[j] = saved + (uRight - param) * temp;
+ saved = (param - uLeft) * temp;
+ }
+ }
+ }
+
+ return n[0];
+ }
+
+
+ ///
+ /// Compute derivatives of basis function 'Nip'.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static double[] DersOneBasisFun(
+ int p,
+ int m,
+ IList knotVector,
+ int i,
+ double u,
+ int n)
+ {
+ // TODO: Check unused m parameter.
+ var ders = new double[n + 1];
+ if (u < knotVector[i] || u >= knotVector[i + p + 1])
+ {
+ for (var k = 0; k <= n; k++)
+ ders[k] = 0.0;
+ return ders;
+ }
+
+ var tmpN = new double[p + 1, p + 1];
+ for (var j = 0; j <= p; j++)
+ {
+ if (u >= knotVector[i + j] && u < knotVector[i + j + 1])
+ tmpN[j, 0] = 1.0;
+ }
+
+ for (var k = 1; k <= p; k++)
+ {
+ double saved;
+ if (tmpN[0, k - 1] == 0.0)
+ saved = 0.0;
+ else
+ {
+ saved = (u - knotVector[i]) * tmpN[0, k - 1]
+ / (knotVector[i + k] - knotVector[i]);
+ }
+
+ for (var j = 0; j < p - k + 1; j++)
+ {
+ var uLeft = knotVector[i + j + 1];
+ var uRight = knotVector[i + j + k + 1];
+ if (tmpN[j + 1, k - 1] == 0.0)
+ {
+ tmpN[j, k] = saved;
+ saved = 0.0;
+ }
+ else
+ {
+ var temp = tmpN[j + 1, k - 1] / (uRight - uLeft);
+ tmpN[j, k] = saved + (uRight - u) * temp;
+ saved = (u - uLeft) * temp;
+ }
+ }
+ }
+
+ ders[0] = tmpN[0, p];
+
+ var tempDers = new double[n + 1];
+ for (var k = 1; k <= n; k++)
+ {
+ for (var j = 0; j <= k; j++)
+ tempDers[j] = tmpN[j, p - k];
+
+ for (var jj = 1; jj <= k; jj++)
+ {
+ double saved;
+ if (tempDers[0] == 0.0)
+ saved = 0.0;
+ else
+ saved = tempDers[0] / (knotVector[i + p - k + jj] - knotVector[i]);
+ for (var j = 0; j < k - jj + 1; j++)
+ {
+ var uLeft = knotVector[i + j + 1];
+ var uRight = knotVector[i + j + p + jj + 1];
+ if (tempDers[j + 1] == 0.0)
+ {
+ tempDers[j] = (p - k + jj) * saved;
+ saved = 0.0;
+ }
+ else
+ {
+ var temp = tempDers[j + 1] / (uRight - uLeft);
+ tempDers[j] = (p - k + jj) * (saved - temp);
+ saved = temp;
+ }
+ }
+ }
+
+ ders[k] = tempDers[0];
+ }
+
+ return ders;
+ }
+
+
+ ///
+ /// Computes a point on a nurbs curve.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Point3d CurvePoint(
+ int n,
+ int p,
+ IList knotVector,
+ IList controlPoints,
+ double u)
+ {
+ var span = FindSpan(n, p, u, knotVector);
+ var basisFunctions = BasisFunctions(span, u, p, knotVector);
+ var c = Point3d.Unset;
+ for (var i = 0; i <= p; i++)
+ c += basisFunctions[i] * controlPoints[span - p + i];
+ return c;
+ }
+
+
+ ///
+ /// Computes the derivatives of a rational b-spline curve at the specified parameter.
+ /// Algorithm A3.2 of 'The Nurbs Book'
+ ///
+ /// Control point count + 1
+ /// Degree
+ /// The curve's knot vector.
+ /// The curve's control points
+ /// Parameter to compute derivatives at.
+ /// Number of derivatives to compute.
+ ///
+ public static Vector3d[] CurveDerivsAlg1(
+ int n,
+ int p,
+ IList knotVector,
+ IList controlPoints,
+ double u,
+ int d)
+ {
+ var ck = new Vector3d[d + 1];
+ var du = Math.Min(d, p);
+ for (var k = p + 1; k <= d; k++)
+ ck[k] = new Vector3d();
+ var span = FindSpan(n, p, u, knotVector);
+ var nDerivatives = DerivativeBasisFunctions(span, u, p, du, knotVector);
+ for (var k = 0; k <= du; k++)
+ {
+ ck[k] = new Vector3d();
+ for (var j = 0; j <= p; j++)
+ ck[k] += nDerivatives[k, j] * ( Vector3d ) controlPoints[span - p + j];
+ }
+
+ return ck;
+ }
+
+
+ ///
+ /// Computes the derivatives of a non-rational b-spline curve at the specified parameter.
+ /// Algorithm A3.2 of 'The Nurbs Book' modified to accept instances..
+ ///
+ /// Control point count + 1
+ /// Degree
+ /// The curve's knot vector.
+ /// The curve's control points
+ /// Parameter to compute derivatives at.
+ /// Number of derivatives to compute.
+ ///
+ public static Point4d[] CurveDerivsAlg1(
+ int n,
+ int p,
+ IList knotVector,
+ IList controlPoints,
+ double u,
+ int d)
+ {
+ var ck = new Point4d[d + 1];
+ var du = Math.Min(d, p);
+ for (var k = p + 1; k <= d; k++)
+ ck[k] = new Point4d();
+ var span = FindSpan(n, p, u, knotVector);
+ var nDerivatives = DerivativeBasisFunctions(span, u, p, du, knotVector);
+ for (var k = 0; k <= du; k++)
+ {
+ ck[k] = new Point4d();
+ for (var j = 0; j <= p; j++)
+ ck[k] += nDerivatives[k, j] * controlPoints[span - p + j];
+ }
+
+ return ck;
+ }
+
+
+ ///
+ /// Computes the control points of all derivative curves up to and including the dth derivative.
+ /// Algorithm A3.3 of 'The Nurbs Book'
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Point3d[,] CurveDerivCpts(
+ int n,
+ int p,
+ IList knotVector,
+ IList controlPoints,
+ int d,
+ int r1,
+ int r2)
+ {
+ var r = r2 - r1;
+ var pk = new Point3d[d + 1, r];
+ for (var i = 0; i <= r; i++)
+ pk[0, i] = controlPoints[r1 + i];
+
+ for (var k = 1; k <= d; k++)
+ {
+ var tmp = p - k + 1;
+ for (var i = 0; i <= r - k; i++)
+ {
+ pk[k, i] = tmp * ( Point3d ) (pk[k - 1, i + 1] - pk[k - 1, i]) /
+ (knotVector[r1 + i + p + 1] - knotVector[r1 + i + k]);
+ }
+ }
+
+ return pk;
+ }
+
+
+ ///
+ /// Computes the derivatives of a rational b-spline curve at the specified parameter.
+ /// Algorithm A3.4 of 'The Nurbs Book'
+ ///
+ /// Control point count + 1
+ /// Degree
+ /// The curve's knot vector.
+ /// The curve's control points
+ /// Parameter to compute derivatives at.
+ /// Number of derivatives to compute.
+ /// The point on a B- spline curve and all derivatives up to and including the dth derivative at a fixed u value.
+ public static Vector3d[] CurveDerivsAlg2(
+ int n,
+ int p,
+ IList knotVector,
+ IList controlPoints,
+ double u,
+ int d)
+ {
+ var du = Math.Min(d, p);
+ var ck = new Vector3d[d + 1];
+ for (var k = p + 1; k <= d; k++)
+ ck[k] = new Vector3d();
+ var span = FindSpan(n, p, u, knotVector);
+ var basisFunctions = AllBasisFuns(span, u, p, knotVector);
+ var pk = CurveDerivCpts(
+ n,
+ p,
+ knotVector,
+ controlPoints,
+ du,
+ span - p,
+ span);
+ for (var k = 0; k <= du; k++)
+ {
+ ck[k] = new Vector3d();
+ for (var j = 0; j <= p - k; j++)
+ ck[k] += basisFunctions[j, p - k] * ( Vector3d ) pk[k, j];
+ }
+
+ return ck;
+ }
+
+
+ ///
+ /// Computes a point on a nurbs surface.
+ /// Algorithm A3.5 of 'The Nurbs Book'
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Point3d SurfacePoint(
+ int n,
+ int p,
+ IList knotVectorU,
+ int m,
+ int q,
+ IList knotVectorV,
+ Matrix controlPoints,
+ double u,
+ double v)
+ {
+ var uspan = FindSpan(n, p, u, knotVectorU);
+ var nU = BasisFunctions(uspan, u, p, knotVectorU);
+ var vspan = FindSpan(m, q, v, knotVectorV);
+ var nV = BasisFunctions(vspan, v, q, knotVectorV);
+ var uind = uspan - p;
+ var surfPt = Point3d.Unset;
+ for (var l = 0; l <= q; l++)
+ {
+ var temp = Point3d.Unset;
+ var vind = vspan - q - l;
+ for (var k = 0; k <= p; k++)
+ temp += nU[k] * controlPoints[uind + k, vind];
+ surfPt += nV[l] * temp;
+ }
+
+ return surfPt;
+ }
+
+
+ ///
+ /// Computes the derivatives of at S(u,v) k times with respect to u and l times with respect to v.
+ /// Algorithm A3.6 of 'The Nurbs Book'
+ ///
+ /// Control point count - 1 in the U direction.
+ /// Degree in the U direction.
+ /// Knot vector in the U direction.
+ /// Control point count - 1 in the V direction.
+ /// Degree in the V direction.
+ /// Knot vector in the V direction.
+ /// Control point grid of the surface.
+ /// U parameter to get derivatives at.
+ /// V parameter to get derivatives at.
+ /// Number of derivatives to compute in each direction.
+ /// A multi dimensional array where [k,l] represents the derivative of S(u,v) with respect to u 'k' times, and v 'l' times.
+ public static Point3d[,] SurfaceDerivsAlg1(
+ int n,
+ int p,
+ IList knotVectorU,
+ int m,
+ int q,
+ IList knotVectorV,
+ Matrix controlPoints,
+ double u,
+ double v,
+ int derivCount)
+ {
+ var skl = new Point3d[derivCount + 1, derivCount + 1];
+ var du = Math.Min(derivCount, p);
+ for (var k = p + 1; k <= derivCount; k++)
+ {
+ for (var l = 0; l <= derivCount - k; l++)
+ skl[k, l] = Point3d.Unset;
+ }
+
+ var dv = Math.Min(derivCount, q);
+ for (var l = q + 1; l <= derivCount; l++)
+ {
+ for (var k = 0; k <= derivCount - 1; k++)
+ skl[k, l] = Point3d.Unset;
+ }
+
+ var uSpan = FindSpan(n, p, u, knotVectorU);
+ var nU = DerivativeBasisFunctions(uSpan, u, p, du, knotVectorU);
+ var vSpan = FindSpan(m, q, v, knotVectorV);
+ var nV = DerivativeBasisFunctions(vSpan, v, q, dv, knotVectorV);
+
+ for (var k = 0; k <= du; k++)
+ {
+ var temp = new Point3d[q];
+ for (var s = 0; s <= q; s++)
+ {
+ temp[s] = Point3d.Unset;
+ for (var r = 0; r <= p; r++)
+ temp[s] += nU[k, r] * controlPoints[uSpan - p + r, vSpan - q + s];
+
+ var dd = Math.Min(derivCount - k, dv);
+
+ for (var l = 0; l <= dd; l++)
+ {
+ skl[k, l] = Point3d.Unset;
+
+ // TODO: Check ss, this was changed from 's' for naming conflicts but it might have been on purpose.
+ for (var ss = 0; ss <= q; ss++)
+ skl[k, l] += nV[l, ss] * temp[ss];
+ }
+ }
+ }
+
+ return skl;
+ }
+
+
+ ///
+ /// Computes the derivatives of at S(u,v) k times with respect to u and l times with respect to v.
+ /// Algorithm A3.6 of 'The Nurbs Book' modified to accept instances.
+ ///
+ /// Control point count - 1 in the U direction.
+ /// Degree in the U direction.
+ /// Knot vector in the U direction.
+ /// Control point count - 1 in the V direction.
+ /// Degree in the V direction.
+ /// Knot vector in the V direction.
+ /// Control point grid of the surface.
+ /// U parameter to get derivatives at.
+ /// V parameter to get derivatives at.
+ /// Number of derivatives to compute in each direction.
+ /// A multi dimensional array where [k,l] represents the derivative of S(u,v) with respect to u 'k' times, and v 'l' times.
+ public static Point4d[,] SurfaceDerivsAlg1(
+ int n,
+ int p,
+ IList knotVectorU,
+ int m,
+ int q,
+ IList knotVectorV,
+ Matrix controlPoints,
+ double u,
+ double v,
+ int derivCount)
+ {
+ var skl = new Point4d[derivCount + 1, derivCount + 1];
+ var du = Math.Min(derivCount, p);
+ for (var k = p + 1; k <= derivCount; k++)
+ {
+ for (var l = 0; l <= derivCount - k; l++)
+ skl[k, l] = new Point4d();
+ }
+
+ var dv = Math.Min(derivCount, q);
+ for (var l = q + 1; l <= derivCount; l++)
+ {
+ for (var k = 0; k <= derivCount - 1; k++)
+ skl[k, l] = new Point4d();
+ }
+
+ var uSpan = FindSpan(n, p, u, knotVectorU);
+ var nU = DerivativeBasisFunctions(uSpan, u, p, du, knotVectorU);
+ var vSpan = FindSpan(m, q, v, knotVectorV);
+ var nV = DerivativeBasisFunctions(vSpan, v, q, dv, knotVectorV);
+
+ for (var k = 0; k <= du; k++)
+ {
+ var temp = new Point4d[q + 1];
+ for (var index = 0; index < temp.Length; index++)
+ temp[index] = new Point4d();
+
+ for (var s = 0; s <= q; s++)
+ {
+ temp[s] = new Point4d();
+ for (var r = 0; r <= p; r++)
+ temp[s] += nU[k, r] * controlPoints[uSpan - p + r, vSpan - q + s];
+
+ var dd = Math.Min(derivCount - k, dv);
+
+ for (var l = 0; l <= dd; l++)
+ {
+ skl[k, l] = new Point4d();
+
+ // TODO: Check ss, this was changed from 's' for naming conflicts but it might have been on purpose.
+ for (var ss = 0; ss <= q; ss++)
+ skl[k, l] += nV[l, ss] * temp[ss];
+ }
+ }
+ }
+
+ return skl;
+ }
+
+
+ ///
+ /// Algorithm A3.7 of 'The Nurbs Book'
+ ///
+ /// Control point count - 1 in the U direction.
+ /// Degree in the U direction.
+ /// Knot vector in the U direction.
+ /// Control point count - 1 in the V direction.
+ /// Degree in the V direction.
+ /// Knot vector in the V direction.
+ /// Control point grid of the surface.
+ /// Number of derivatives to compute.
+ ///
+ ///
+ ///
+ ///
+ /// Computed derivatives at S(u,v).
+ public static Point3d[][][][] SurfaceDerivCpts(
+ int n,
+ int p,
+ IList knotVectorU,
+ int m,
+ int q,
+ IList knotVectorV,
+ Matrix controlPoints,
+ int d,
+ int r1,
+ int r2,
+ int s1,
+ int s2)
+ {
+ var pkl = new Point3d[d][][][];
+
+ var du = Math.Min(d, p);
+ var dv = Math.Min(d, q);
+ var r = r2 - r1;
+ var s = s2 - s1;
+
+ for (var j = s1; j <= s2; j++)
+ {
+ var temp = CurveDerivCpts(
+ n,
+ p,
+ knotVectorU,
+ controlPoints.Column(j),
+ du,
+ r1,
+ r2);
+ for (var k = 0; k <= du; k++)
+ {
+ for (var i = 0; i <= r - k; i++)
+ pkl[k][0][i][j - s1] = temp[k, i];
+ }
+ }
+
+ for (var k = 0; k <= du; k++)
+ {
+ for (var i = 0; i <= r - k; i++)
+ {
+ var dd = Math.Min(d - k, dv);
+ var temp = CurveDerivCpts(
+ m,
+ q,
+ knotVectorV,
+ pkl[k][0][i],
+ dd,
+ 0,
+ s);
+ for (var l = i; k <= dd; k++)
+ {
+ for (var j = 0; j <= s - l; j++)
+ pkl[k][l][i][j] = temp[l, j];
+ }
+ }
+ }
+
+ return pkl;
+ }
+
+
+ ///
+ /// Algorithm A3.8 of 'The Nurbs Book'
+ ///
+ /// Control point count - 1 in the U direction.
+ /// Degree in the U direction.
+ /// Knot vector in the U direction.
+ /// Control point count - 1 in the V direction.
+ /// Degree in the V direction.
+ /// Knot vector in the V direction.
+ /// Control point grid of the surface.
+ /// U parameter to get derivatives at.
+ /// V parameter to get derivatives at.
+ /// Number of derivatives to compute in each direction.
+ /// A multi dimensional array where [k,l] represents the derivative of S(u,v) with respect to u 'k' times, and v 'l' times.
+ public static Point3d[,] SurfaceDerivsAlg2(
+ int n,
+ int p,
+ IList knotVectorU,
+ int m,
+ int q,
+ IList knotVectorV,
+ Matrix controlPoints,
+ double u,
+ double v,
+ int d)
+ {
+ var skl = new Point3d[d + 1, d + 1];
+
+ var du = Math.Min(d, p);
+ for (var k = p + 1; k <= d; k++)
+ {
+ for (var l = 0; l <= d - k; l++)
+ skl[k, l] = Point3d.Unset;
+ }
+
+ var dv = Math.Min(d, q);
+ for (var l = q + 1; l <= d; l++)
+ {
+ for (var k = 0; k <= d - l; k++)
+ skl[k, l] = Point3d.Unset;
+ }
+
+ var uSpan = FindSpan(n, p, u, knotVectorU);
+ var nV = AllBasisFuns(uSpan, u, p, knotVectorU);
+ var vSpan = FindSpan(m, q, v, knotVectorV);
+ var nU = AllBasisFuns(vSpan, v, q, knotVectorV);
+ var pkl = SurfaceDerivCpts(
+ n,
+ p,
+ knotVectorU,
+ m,
+ q,
+ knotVectorV,
+ controlPoints,
+ d,
+ uSpan - p,
+ uSpan,
+ vSpan - q,
+ vSpan);
+
+ for (var k = 0; k <= du; k++)
+ {
+ var dd = Math.Min(d - k, dv);
+ for (var l = 0; l <= dd; l++)
+ {
+ skl[k, l] = Point3d.Unset;
+ for (var i = 0; i <= q - l; i++)
+ {
+ var tmp = Point3d.Unset;
+ for (var j = 0; j <= p - k; j++)
+ tmp += nV[j, p - k] * pkl[k][l][j][i];
+ skl[k, l] += nU[i, q - l] * tmp;
+ }
+ }
+ }
+
+ return skl;
+ }
+
+
+ ///
+ /// Computes a point on a nurbs curve
+ /// Algorithm A4.1 of 'The Nurbs Book'
+ ///
+ /// Number of control points - 1
+ /// Degree. Cannot be bigger or equals the number of control points, nor smaller than 1.
+ /// List of numbers representing the knot vector of the curve.
+ /// The control points for the curve.
+ /// Parameter along the curve to compute the point at.
+ /// A point along the curve.
+ public static Point3d CurvePoint(
+ int n,
+ int p,
+ IList knotVector,
+ IList controlPoints,
+ double u)
+ {
+ var span = FindSpan(n, p, u, knotVector);
+ var basisFunctions = BasisFunctions(span, u, p, knotVector);
+ var cw = new Point4d();
+ for (var j = 0; j <= p; j++)
+ cw += basisFunctions[j] * controlPoints[span - p + j];
+ return ( Point3d ) cw;
+ }
+
+
+ ///
+ /// Computes a binomial coefficient of a NURBS curve.
+ ///
+ ///
+ ///
+ /// Computed coefficient.
+ public static double BinomialCoefficient(int n, int k)
+ {
+ if (n - k == 1 || k == 1)
+ return n;
+
+ var b = new double[n + 1, n - k + 1];
+ for (var i = 1; i < b.GetLength(0); i++)
+ {
+ for (var j = 0; j < b.GetLength(1); j++)
+ {
+ if (i == j || j == 0)
+ b[i, j] = 1;
+ else if (j == 1 || i - j == 1)
+ b[i, j] = i;
+ else
+ b[i, j] = b[i - 1, j - 1] + b[i - 1, j];
+ }
+ }
+
+ return b[n, n - k];
+ }
+
+
+ ///
+ /// Determines if a knot vector is clamped.
+ ///
+ /// Knot vector.
+ /// Degree.
+ /// True if knot vector is clamped.
+ public static bool IsClamped(IList u, int p) =>
+ u[0] == u[p] && u[u.Count - 1] == u[u.Count - 1 - p];
+
+
+ ///
+ /// Compute C(u) derivatives from Cw(u) derivatives.
+ /// Algorithm A4.2 of 'The Nurbs Book'
+ ///
+ /// Position derivatives.
+ /// Weight derivatives.
+ /// Derivative count.
+ /// Computed derivatives.
+ public static Vector3d[] RatCurveDerivs(IList aDers, IList wDers, int d)
+ {
+ var ders = new Vector3d[d + 1];
+
+ for (var k = 0; k <= d; k++)
+ {
+ var v = aDers[k];
+ for (var i = 1; i <= k; i++)
+ v -= BinomialCoefficient(k, i) * wDers[i] * ders[k - i];
+
+ ders[k] = v / wDers[0];
+ }
+
+ return ders;
+ }
+
+
+ public static Vector3d[] NurbsCurveDerivs(
+ int n,
+ int p,
+ IList knotVector,
+ IList controlPoints,
+ double u,
+ int d)
+ {
+ var ck1 = CurveDerivsAlg1(n, p, knotVector, controlPoints, u, d);
+ var aDers = ck1.Select(der => ( Vector3d ) der.Position).ToList();
+ var wDers = ck1.Select(der => der.Weight).ToList();
+ return RatCurveDerivs(aDers, wDers, d);
+ }
+
+
+ ///
+ /// Compute a point on a nurbs surface.
+ /// Algorithm A4.3 of 'The Nurbs Book'
+ ///
+ /// The number of control points in the U direction - 1
+ /// The degree of the surface in the U direction.
+ /// The knot vector for the U direction.
+ /// The number of control points in the V direction - 1
+ /// The degree of the surface in the V direction.
+ /// The knot vector for the V direction.
+ /// A 2-dimensional matrix/grid of control points.
+ /// Parameter to compute the point at in the U direction.
+ /// Parameter to compute the point at in the V direction.
+ /// A instance with the result.
+ public static Point3d SurfacePoint(
+ int n,
+ int p,
+ IList knotVectorU,
+ int m,
+ int q,
+ IList knotVectorV,
+ Matrix controlPoints,
+ double u,
+ double v)
+ {
+ var uspan = FindSpan(n, p, u, knotVectorU);
+ var nU = BasisFunctions(uspan, u, p, knotVectorU);
+ var vspan = FindSpan(m, q, v, knotVectorV);
+ var nV = BasisFunctions(vspan, v, q, knotVectorV);
+
+ var temp = new Point4d[q + 1];
+ for (var l = 0; l <= q; l++)
+ {
+ temp[l] = new Point4d();
+ for (var k = 0; k <= p; k++)
+ temp[l] += nU[k] * controlPoints[uspan - p + k, vspan - q + l];
+ }
+
+ var sW = new Point4d();
+ for (var l = 0; l <= q; l++)
+ sW += nV[l] * temp[l];
+
+ return ( Point3d ) sW;
+ }
+
+
+ ///
+ /// Computes the derivatives of a non-rational b-spline curve from the decomposed derivatives of a rational surface.
+ /// Algorithm A4.4 of 'The Nurbs Book'
+ ///
+ /// Position derivatives
+ /// Weight derivatives
+ /// Derivative count.
+ /// Computed derivatives at S(u,v)
+ public static Matrix RatSurfaceDerivs(
+ Matrix aDers,
+ Matrix wDers,
+ int d)
+ {
+ var skl = new Matrix(wDers.M, wDers.N);
+ for (var m = 0; m < wDers.M; m++)
+ {
+ for (var n = 0; n < wDers.N; n++)
+ skl[m, n] = new Vector3d();
+ }
+
+ var a = wDers.M - 1;
+ var b = wDers.N - 1;
+
+ for (var k = 0; k <= a; k++)
+ {
+ for (var l = 0; l <= b; l++)
+ {
+ var v = aDers[k, l];
+ for (var j = 0; j <= l; j++)
+ v -= BinomialCoefficient(l, j) * wDers[0, j] * skl[k, l - j];
+
+ for (var i = 0; i <= k; i++)
+ {
+ v -= BinomialCoefficient(k, i) * wDers[i, 0] * skl[k - i, l];
+ var v2 = new Vector3d();
+ for (var j = 0; j <= l; j++)
+ v2 += BinomialCoefficient(l, j) * wDers[i, j] * skl[k - i, l - j];
+
+ v -= BinomialCoefficient(k, i) * v2;
+ }
+
+ skl[k, l] = v / wDers[0, 0];
+ }
+ }
+
+ return skl;
+ }
+
+
+ ///
+ /// Compute C(u) derivatives from Cw(u) derivatives.
+ ///
+ /// The number of control points in the U direction - 1
+ /// The degree of the surface in the U direction.
+ /// The knot vector for the U direction.
+ /// The number of control points in the V direction - 1
+ /// The degree of the surface in the V direction.
+ /// The knot vector for the V direction.
+ /// A 2-dimensional matrix/grid of control points.
+ /// U parameter.
+ /// V parameter.
+ /// Derivative count.
+ /// Computed derivatives.
+ public static Matrix NurbsSurfaceDerivs(
+ int n,
+ int p,
+ IList knotVectorU,
+ int m,
+ int q,
+ IList knotVectorV,
+ Matrix controlPoints,
+ double u,
+ double v,
+ int d)
+ {
+ var skl1 = SurfaceDerivsAlg1(
+ n,
+ p,
+ knotVectorU,
+ m,
+ q,
+ knotVectorV,
+ controlPoints,
+ u,
+ v,
+ d);
+
+ var aDers = new Matrix(skl1.GetLength(0) + 1, skl1.GetLength(1) + 1);
+ var wDers = new Matrix(skl1.GetLength(0) + 1, skl1.GetLength(1) + 1);
+
+ for (var i = 0; i < controlPoints.M - 1; i++)
+ {
+ for (var j = 0; j < controlPoints.N - 1; j++)
+ {
+ wDers[i, j] = controlPoints[i, j].Weight;
+ aDers[i, j] = controlPoints[i, j].Position;
+ }
+ }
+
+ return RatSurfaceDerivs(aDers, wDers, d);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Geometry/3D/NurbsCurve.cs b/src/Geometry/3D/NurbsCurve.cs
new file mode 100644
index 0000000..a940d35
--- /dev/null
+++ b/src/Geometry/3D/NurbsCurve.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Paramdigma.Core.Geometry
+{
+ ///
+ ///
+ public class NurbsCurve : BaseCurve
+ {
+ ///
+ /// The control points of the nurbs curve.
+ ///
+ public List ControlPoints;
+
+ ///
+ /// The degree of the curve.
+ ///
+ public int Degree;
+
+ ///
+ /// The nurbs curve knot vector.
+ ///
+ public List Knots;
+
+
+ ///
+ /// Initializes a new instance of by it's control points and degree.
+ ///
+ /// The control points to create the curve with.
+ /// The desired degree of the curve. Degree cannot be > (ControlPoints - 1)
+ public NurbsCurve(List controlPoints, int degree)
+ {
+ this.ControlPoints = controlPoints;
+ this.Knots = NurbsCalculator.CreateUniformKnotVector(controlPoints.Count, degree)
+ .ToList();
+ this.Degree = degree;
+ }
+
+
+ ///
+ /// Gets the count of the control points - 1.
+ ///
+ private int N => this.ControlPoints.Count - 1;
+
+ ///
+ /// The start point of the curve.
+ ///
+ public Point3d StartPoint => this.PointAt(this.Domain.Start);
+
+ ///
+ /// The end point of the curve.
+ ///
+ public Point3d EndPoint => this.PointAt(this.Domain.End);
+
+ ///
+ /// The tangent vector at the start of the curve.
+ ///
+ public Vector3d StartTangent => this.TangentAt(this.Domain.Start);
+
+ ///
+ /// The tangent vector at the end of the curve.
+ ///
+ public Vector3d EndTangent => this.TangentAt(this.Domain.End);
+
+
+ ///
+ /// Computes the specific amount of derivatives on the specified parameter.
+ ///
+ /// Parameter to compute derivatives at.
+ /// Number of derivatives to compute.
+ /// Array containing the
+ private IList DerivativesAt(double t, int count) =>
+ NurbsCalculator.NurbsCurveDerivs(
+ this.N,
+ this.Degree,
+ this.Knots,
+ this.ControlPoints,
+ t,
+ count
+ );
+
+
+ ///
+ public override Point3d PointAt(double t) =>
+ NurbsCalculator.CurvePoint(this.N, this.Degree, this.Knots, this.ControlPoints, t);
+
+
+ ///
+ public override Vector3d TangentAt(double t) => this.DerivativesAt(t, 1)[1].Unit();
+
+
+ ///
+ public override Vector3d NormalAt(double t) => this.DerivativesAt(t, 2)[2].Unit();
+
+
+ ///
+ public override Vector3d BinormalAt(double t) => this.DerivativesAt(t, 3)[3].Unit();
+
+
+ ///
+ public override Plane FrameAt(double t)
+ {
+ var ders = this.DerivativesAt(t, 3);
+ return new Plane(( Point3d ) ders[0], ders[1], ders[2], ders[3]);
+ }
+
+
+ ///
+ public override bool CheckValidity() => true;
+
+
+ ///
+ protected override double ComputeLength() => throw new NotImplementedException();
+ }
+}
\ No newline at end of file
diff --git a/src/Geometry/3D/NurbsSurface.cs b/src/Geometry/3D/NurbsSurface.cs
new file mode 100644
index 0000000..6c34225
--- /dev/null
+++ b/src/Geometry/3D/NurbsSurface.cs
@@ -0,0 +1,153 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Paramdigma.Core.Collections;
+using Paramdigma.Core.Geometry.Interfaces;
+
+namespace Paramdigma.Core.Geometry
+{
+ ///
+ /// Represents a NURBS surface. Contains properties and methods for operating with NURBS surfaces.
+ ///
+ public class NurbsSurface : ISurface
+ {
+ ///
+ /// The surface's grid of control points.
+ ///
+ public readonly Matrix ControlPoints;
+
+ ///
+ /// Surface's degree in the U direction
+ ///
+ public readonly int DegreeU;
+
+ ///
+ /// Surface's degree in the V direction
+ ///
+ public readonly int DegreeV;
+
+ ///
+ /// Knot vector in the U direction.
+ ///
+ public readonly List KnotsU;
+
+ ///
+ /// Knot vector in the V direction.
+ ///
+ public readonly List KnotsV;
+
+
+ ///
+ /// Initializes a new instance of by it's control points and degrees.
+ ///
+ /// Grid of control points for the surface.
+ /// Degree of the surface in the U direction.
+ /// Degree of the surface in the V direction.
+ public NurbsSurface(Matrix controlPoints, int degreeU, int degreeV)
+ {
+ this.ControlPoints = controlPoints;
+
+ this.DegreeU = degreeU;
+ this.DegreeV = degreeV;
+
+ this.KnotsU = NurbsCalculator
+ .CreateUniformKnotVector(this.ControlPoints.N, this.DegreeU)
+ .ToList();
+ this.KnotsV = NurbsCalculator
+ .CreateUniformKnotVector(this.ControlPoints.M, this.DegreeV)
+ .ToList();
+ }
+
+
+ ///
+ public Interval DomainU => new Interval(this.KnotsU[0], this.KnotsU[this.KnotsU.Count - 1]);
+
+ ///
+ public Interval DomainV => new Interval(this.KnotsV[0], this.KnotsV[this.KnotsV.Count - 1]);
+
+
+ ///
+ public Point3d PointAt(double u, double v) => NurbsCalculator.SurfacePoint(
+ this.ControlPoints.N - 1,
+ this.DegreeU,
+ this.KnotsU,
+ this.ControlPoints.M - 1,
+ this.DegreeV,
+ this.KnotsV,
+ this.ControlPoints,
+ u,
+ v);
+
+
+ ///
+ public Vector3d NormalAt(double u, double v) => throw new NotImplementedException();
+
+
+ ///
+ public Plane FrameAt(double u, double v) => throw new NotImplementedException();
+
+
+ ///
+ public double DistanceTo(Point3d point) => throw new NotImplementedException();
+
+
+ ///
+ public Point3d ClosestPointTo(Point3d point) => throw new NotImplementedException();
+
+
+ ///
+ /// Creates a square flat surface on the XY Plane.
+ ///
+ /// Dimension interval on the X direction.
+ /// Dimension interval on the Y direction.
+ /// Number of points on the X direction.
+ /// Number of points on the Y direction.
+ /// Flat nurbs surface.
+ public static NurbsSurface CreateFlatSurface(
+ Interval xDimension,
+ Interval yDimension,
+ int xCount,
+ int yCount)
+ {
+ var rnd = new Random();
+ var wDomain = new Interval(1, 5);
+ var m = new Matrix(xCount, yCount);
+ for (var i = 0; i < xCount; i++)
+ {
+ for (var j = 0; j < yCount; j++)
+ {
+ m[i, j] = new Point4d(
+ xDimension.RemapFromUnit(( double ) i / xCount),
+ yDimension.RemapFromUnit(( double ) j / yCount),
+ 0,
+ 1);
+ }
+ }
+
+ var degreeU = xCount <= 3 ? xCount - 1 : 3;
+ var degreeV = yCount <= 3 ? yCount - 1 : 3;
+ return new NurbsSurface(m, degreeU, degreeV);
+ }
+
+
+ ///
+ /// Computes the derivatives at at S(u,v).
+ ///
+ /// U parameter to compute at.
+ /// V parameter to compute at.
+ /// Number of derivatives to compute.
+ /// Computed derivatives.
+ public Matrix DerivativesAt(double u, double v, int count) =>
+ NurbsCalculator.NurbsSurfaceDerivs(
+ this.ControlPoints.M - 1,
+ this.DegreeU,
+ this.KnotsU,
+ this.ControlPoints.M - 1,
+ this.DegreeV,
+ this.KnotsV,
+ this.ControlPoints,
+ u,
+ v,
+ count);
+ }
+}
\ No newline at end of file
diff --git a/src/Geometry/3D/Plane.cs b/src/Geometry/3D/Plane.cs
index f48da6a..fec1132 100644
--- a/src/Geometry/3D/Plane.cs
+++ b/src/Geometry/3D/Plane.cs
@@ -15,7 +15,12 @@ public class Plane
///
/// Plane to copy values from.
public Plane(Plane plane)
- : this(new Point3d(plane.Origin), new Vector3d(plane.XAxis), new Vector3d(plane.YAxis), new Vector3d(plane.ZAxis)) { }
+ : this(
+ new Point3d(plane.Origin),
+ new Vector3d(plane.XAxis),
+ new Vector3d(plane.YAxis),
+ new Vector3d(plane.ZAxis)) { }
+
///
/// Initializes a new instance of the class given it's origin at the specified point.
@@ -24,6 +29,7 @@ public Plane(Plane plane)
public Plane(Point3d origin)
: this(origin, Vector3d.UnitX, Vector3d.UnitY) { }
+
///
/// Initializes a new instance of the class given a point and two vectors.
/// Vectors do not necessarily have to be perpendicular.
@@ -35,6 +41,7 @@ public Plane(Point3d origin)
public Plane(Point3d origin, Vector3d xAxis, Vector3d yAxis)
: this(origin, xAxis, yAxis, xAxis.Cross(yAxis)) { }
+
///
/// Initializes a new instance of the class given a point and three vectors.
/// Will throw an error if vectors are not perpendicular to each other.
@@ -51,6 +58,7 @@ public Plane(Point3d origin, Vector3d xAxis, Vector3d yAxis, Vector3d zAxis)
this.ZAxis = zAxis;
}
+
///
/// Initializes a new instance of the class given 3 non co-linear points.
///
@@ -77,66 +85,61 @@ public Plane(Point3d ptA, Point3d ptB, Point3d ptC)
this.ZAxis = normal;
}
+
///
/// Gets or sets the plane origin.
///
///
- public Point3d Origin
- {
- get;
- set;
- }
+ public Point3d Origin { get; set; }
///
/// Gets or sets the plane X axis.
///
///
- public Vector3d XAxis
- {
- get;
- set;
- }
+ public Vector3d XAxis { get; set; }
///
/// Gets or sets the plane Y axis.
///
///
- public Vector3d YAxis
- {
- get;
- set;
- }
+ public Vector3d YAxis { get; set; }
///
/// Gets or sets the plane Z axis.
///
///
- public Vector3d ZAxis
- {
- get;
- set;
- }
+ public Vector3d ZAxis { get; set; }
///
/// Gets plane with axis' UnitX and UnitY.
///
///
- public static Plane WorldXY => new Plane(Point3d.WorldOrigin, Vector3d.UnitX, Vector3d.UnitY);
+ public static Plane WorldXY => new Plane(
+ Point3d.WorldOrigin,
+ Vector3d.UnitX,
+ Vector3d.UnitY);
///
/// Gets plane with axis' UnitX and UnitZ.
///
///
- public static Plane WorldXZ => new Plane(Point3d.WorldOrigin, Vector3d.UnitX, Vector3d.UnitZ);
+ public static Plane WorldXZ => new Plane(
+ Point3d.WorldOrigin,
+ Vector3d.UnitX,
+ Vector3d.UnitZ);
///
/// Gets plane with axis' UnitY and UnitZ.
///
///
- public static Plane WorldYZ => new Plane(Point3d.WorldOrigin, Vector3d.UnitY, Vector3d.UnitZ);
+ public static Plane WorldYZ => new Plane(
+ Point3d.WorldOrigin,
+ Vector3d.UnitY,
+ Vector3d.UnitZ);
// TODO: Add utility methods to Plane class (flip Axis, relative coordinates...)
+
///
/// Flips the plane by interchanging the X and Y axis and negating the Z axis.
///
@@ -148,6 +151,7 @@ public void Flip()
this.ZAxis = -this.ZAxis;
}
+
///
/// Computes the point at the specified Plane parameters.
///
@@ -156,6 +160,7 @@ public void Flip()
///
public Point3d PointAt(double u, double v) => this.PointAt(u, v, 0);
+
///
/// Computes a 3D point in the coordinate space of this plane.
///
@@ -163,7 +168,9 @@ public void Flip()
/// Coordinate for the Y axis.
/// Coordinate for the Z axis.
/// Computed point.
- public Point3d PointAt(double u, double v, double w) => this.Origin + ((u * this.XAxis) + (v * this.YAxis) + (w * this.ZAxis));
+ public Point3d PointAt(double u, double v, double w) =>
+ this.Origin + (u * this.XAxis + v * this.YAxis + w * this.ZAxis);
+
///
/// Remap a given point to this plane's coordinate system.
@@ -180,12 +187,15 @@ public Point3d RemapToPlaneSpace(Point3d point)
return new Point3d(u, v, w);
}
+
///
/// Remap a given point to the XY Plane coordiante system.
///
/// Point to remap.
/// Point with relative coordinates to the plane.
- public Point3d RemapToWorldXYSpace(Point3d point) => this.Origin + (point.X * this.XAxis) + (point.Y * this.YAxis) + (point.Z * this.ZAxis);
+ public Point3d RemapToWorldXYSpace(Point3d point) =>
+ this.Origin + point.X * this.XAxis + point.Y * this.YAxis + point.Z * this.ZAxis;
+
///
/// Project a point to the plane.
@@ -201,6 +211,7 @@ public Point3d ClosestPoint(Point3d point)
return this.PointAt(u, v);
}
+
///
/// Compute the distance from a point to the plane.
///
@@ -208,36 +219,45 @@ public Point3d ClosestPoint(Point3d point)
/// Distance to point.
public double DistanceTo(Point3d point) => (point - this.Origin).Dot(this.ZAxis);
+
///
/// Returns the parametric equation for this plane.
///
/// List with equation values.
public double[] GetPlaneEquation() => throw new NotImplementedException();
+
///
/// Performs a deep copy of this plane.
///
/// Plane clone.
- public Plane Clone() => new Plane(new Point3d(this.Origin), new Vector3d(this.XAxis), new Vector3d(this.YAxis), new Vector3d(this.ZAxis));
+ public Plane Clone() => new Plane(
+ new Point3d(this.Origin),
+ new Vector3d(this.XAxis),
+ new Vector3d(this.YAxis),
+ new Vector3d(this.ZAxis));
+
+ ///
public override bool Equals(object obj)
{
if (!(obj is Plane))
return false;
- var plane = (Plane)obj;
+ var plane = ( Plane ) obj;
return plane.Origin == this.Origin
&& plane.XAxis == this.XAxis
&& plane.YAxis == this.YAxis;
}
+
///
public override int GetHashCode()
{
unchecked
{
// Choose large primes to avoid hashing collisions
- const int hashingBase = (int)2166136261;
+ const int hashingBase = ( int ) 2166136261;
const int hashingMultiplier = 16777619;
var hash = hashingBase;
diff --git a/src/Geometry/3D/Point3d.cs b/src/Geometry/3D/Point3d.cs
index 63ca23d..862b917 100644
--- a/src/Geometry/3D/Point3d.cs
+++ b/src/Geometry/3D/Point3d.cs
@@ -13,6 +13,7 @@ public class Point3d : BasePoint
/// .
public Point3d() { }
+
///
/// Initializes a new instance of the class by cartesian coordinates.
///
@@ -23,6 +24,7 @@ public Point3d() { }
public Point3d(double xCoord, double yCoord, double zCoord)
: base(xCoord, yCoord, zCoord) { }
+
///
/// Initializes a new instance of the class from a 2-dimensional point.
///
@@ -31,6 +33,7 @@ public Point3d(double xCoord, double yCoord, double zCoord)
public Point3d(Point2d point)
: base(point.X, point.Y, 0) { }
+
///
/// Initializes a new instance of the class.
///
@@ -39,8 +42,10 @@ public Point3d(Point2d point)
public Point3d(Point3d point)
: base(point) { }
+
///
- /// Initializes a new instance of the class from a 4-dimensional point by dividing the cartesian
+ /// Initializes a new instance of the class from a 4-dimensional point by
+ /// dividing the cartesian
/// coordinates by the weight.
///
/// 4d point to convert.
@@ -48,6 +53,7 @@ public Point3d(Point3d point)
public Point3d(Point4d point)
: this(point.X / point.Weight, point.Y / point.Weight, point.Z / point.Weight) { }
+
///
/// Gets a new Unset point.
///
@@ -60,35 +66,47 @@ 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.
///
/// Point.
///
- public double DistanceTo(Point3d point) => Math.Sqrt(Math.Pow(point.X - this.X, 2) + Math.Pow(point.Y - this.Y, 2) + Math.Pow(point.Z - this.Z, 2));
+ public double DistanceTo(Point3d point) => Math.Sqrt(
+ Math.Pow(point.X - this.X, 2) + Math.Pow(point.Y - this.Y, 2)
+ + Math.Pow(point.Z - this.Z, 2));
+
///
public override bool Equals(object obj) => base.Equals(obj);
+
///
public override int GetHashCode() => base.GetHashCode();
+
///
public override string ToString() => "Point3d" + base.ToString();
+
///
/// Adds a vector to a point.
///
/// Point.
/// Vector.
/// .
- public static Point3d operator +(Point3d point, Vector3d v) => new Point3d(point.X + v.X, point.Y + v.Y, point.Z + v.Z);
+ public static Point3d operator +(Point3d point, Vector3d v) => new Point3d(
+ point.X + v.X,
+ point.Y + v.Y,
+ point.Z + v.Z);
+
///
/// Substracts a point from another point.
@@ -96,7 +114,11 @@ public Point3d(Point4d point)
/// Point A.
/// Point B.
/// .
- public static Vector3d operator -(Point3d point, Point3d point2) => new Vector3d(point.X - point2.X, point.Y - point2.Y, point.Z - point2.Z);
+ public static Vector3d operator -(Point3d point, Point3d point2) => new Vector3d(
+ point.X - point2.X,
+ point.Y - point2.Y,
+ point.Z - point2.Z);
+
///
/// Substracts a vector from a point.
@@ -104,7 +126,11 @@ public Point3d(Point4d point)
/// Point.
/// Vector.
/// .
- public static Vector3d operator -(Point3d point, Vector3d vector) => new Vector3d(point.X - vector.X, point.Y - vector.Y, point.Z - vector.Z);
+ public static Vector3d operator -(Point3d point, Vector3d vector) => new Vector3d(
+ point.X - vector.X,
+ point.Y - vector.Y,
+ point.Z - vector.Z);
+
///
/// Negates a point.
@@ -117,13 +143,18 @@ public Point3d(Point4d point)
point.Y != 0 ? -point.Y : 0,
point.Z != 0 ? -point.Z : 0);
+
///
/// Multiplies a point with a number.
///
/// Point.
/// Number.
/// .
- public static Point3d operator *(Point3d point, double scalar) => new Point3d(point.X * scalar, point.Y * scalar, point.Z * scalar);
+ public static Point3d operator *(Point3d point, double scalar) => new Point3d(
+ point.X * scalar,
+ point.Y * scalar,
+ point.Z * scalar);
+
///
/// Multiplies a point with a number.
@@ -131,7 +162,11 @@ public Point3d(Point4d point)
/// Number.
/// Point.
/// .
- public static Point3d operator *(double scalar, Point3d point) => new Point3d(point.X * scalar, point.Y * scalar, point.Z * scalar);
+ public static Point3d operator *(double scalar, Point3d point) => new Point3d(
+ point.X * scalar,
+ point.Y * scalar,
+ point.Z * scalar);
+
///
/// Divides a point with a number.
@@ -139,7 +174,12 @@ public Point3d(Point4d point)
/// Point.
/// Number.
/// .
- public static Point3d operator /(Point3d point, double scalar) => new Point3d(point.X / scalar, point.Y / scalar, point.Z / scalar);
+ public static Point3d operator /(Point3d point, double scalar) =>
+ new Point3d(
+ point.X / scalar,
+ point.Y / scalar,
+ point.Z / scalar);
+
///
/// Checks equality between two points.
@@ -147,7 +187,9 @@ public Point3d(Point4d point)
/// Point A.
/// Point B.
/// .
- public static bool operator ==(Point3d point, Point3d point2) => point != null && point.Equals(point2);
+ public static bool operator ==(Point3d point, Point3d point2) =>
+ point != null && point.Equals(point2);
+
///
/// Checks inequality between two points.
@@ -155,26 +197,34 @@ public Point3d(Point4d point)
/// Point A.
/// Point B.
/// .
- public static bool operator !=(Point3d point, Point3d point2) => !point.Equals(point2);
+ public static bool operator !=(Point3d point, Point3d point2) =>
+ !point?.Equals(point2) ?? true;
+
// Implicit conversions
+
///
/// Explicit conversion from vector to point.
///
/// 3d Vector to convert.
- public static explicit operator Point3d(Vector3d v) => new Point3d(v.X, v.Y, v.Z);
+ public static explicit operator Point3d(Vector3d v) =>
+ new Point3d(v.X, v.Y, v.Z);
+
///
/// Implicit conversion from point to vector.
///
/// 3d Point to convert.
- public static implicit operator Vector3d(Point3d pt) => new Vector3d(pt.X, pt.Y, pt.Z);
+ public static implicit operator Vector3d(Point3d pt) =>
+ new Vector3d(pt.X, pt.Y, pt.Z);
+
///
/// Explicit conversion from 4-dimensional point to 3-dimensional point. (X/W,Y/W,Z/W).
///
/// 3d Point to convert.
- public static explicit operator Point3d(Point4d point) => new Point3d(point);
+ public static explicit operator Point3d(Point4d point) =>
+ new Point3d(point);
}
}
\ No newline at end of file
diff --git a/src/Geometry/3D/Point4d.cs b/src/Geometry/3D/Point4d.cs
index a9f239c..868f140 100644
--- a/src/Geometry/3D/Point4d.cs
+++ b/src/Geometry/3D/Point4d.cs
@@ -9,14 +9,17 @@ public class Point4d : BasePoint
{
private double weight;
+
///
/// Initializes a new instance of the class.
///
/// Point with all values to zero.
public Point4d() => this.weight = 0;
+
///
- /// Initializes a new instance of the class by cartesian coordinates and weight.
+ /// Initializes a new instance of the class by cartesian coordinates and
+ /// weight.
///
/// X Coordinate.
/// Y Coordinate.
@@ -27,8 +30,10 @@ public Point4d(double x, double y, double z, double w)
: base(x, y, z) =>
this.weight = w;
+
///
- /// Initializes a new instance of the class from a 3-dimensional point and a weight.
+ /// Initializes a new instance of the class from a 3-dimensional point and a
+ /// weight.
///
/// Point.
/// Weight.
@@ -37,8 +42,10 @@ public Point4d(Point3d pt, double w)
: base(pt) =>
this.weight = w;
+
///
- /// Initializes a new instance of the class from a 3-dimensional point and a weight.
+ /// Initializes a new instance of the class from a 3-dimensional point and a
+ /// weight.
///
/// Point.
/// New 4-dimensional point with the specified values.
@@ -46,6 +53,7 @@ public Point4d(Point3d pt)
: base(pt) =>
this.weight = 1;
+
///
/// Gets or sets the weight of this point.
///
@@ -65,47 +73,136 @@ public double Weight
///
public Point3d Position => new Point3d(this.X, this.Y, this.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);
- ///
- 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);
+ ///
+ /// Adds to points together.
+ ///
+ /// First point to add.
+ /// Second point to add.
+ /// A new instance.
+ 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);
- ///
- public static Point4d operator -(Point4d point) => new Point4d(-point.X, -point.Y, -point.Z, point.Weight);
- ///
- public static Point4d operator *(Point4d point, double scalar) => new Point4d(point.X * scalar, point.Y * scalar, point.Z * scalar, point.Weight * scalar);
+ ///
+ /// Subtracts one point from another.
+ ///
+ /// Point to subtract from.
+ /// Point to be subtracted.
+ /// A new instance.
+ 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);
- ///
- public static Point4d operator *(double scalar, Point4d point) => new Point4d(point.X * scalar, point.Y * scalar, point.Z * scalar, point.Weight * scalar);
- ///
- public static Point4d operator /(Point4d point, double scalar) => new Point4d(point.X / scalar, point.Y / scalar, point.Z / scalar, point.Weight / scalar);
+ ///
+ /// Negates a point.
+ ///
+ /// Point to be negated.
+ /// A new instance.
+ public static Point4d operator -(Point4d point) => new Point4d(
+ -point.X,
+ -point.Y,
+ -point.Z,
+ point.Weight);
- ///
- public static bool operator ==(Point4d point, Point4d point2) => point.Equals(point2);
- ///
- public static bool operator !=(Point4d point, Point4d point2) => !point.Equals(point2);
+ ///
+ /// Multiply a point by a scalar.
+ ///
+ /// Point to be multiplied.
+ /// Number to multiply point with.
+ /// A new instance.
+ public static Point4d operator *(Point4d point, double scalar) => new Point4d(
+ point.X * scalar,
+ point.Y * scalar,
+ point.Z * scalar,
+ point.Weight * scalar);
+
+
+ ///
+ /// Multiply a point by a scalar.
+ ///
+ /// Number to multiply point with.
+ /// Point to be multiplied.
+ /// A new instance.
+ public static Point4d operator *(double scalar, Point4d point) => new Point4d(
+ point.X * scalar,
+ point.Y * scalar,
+ point.Z * scalar,
+ point.Weight * scalar);
+
+
+ ///
+ /// Divides a point by a scalar value.
+ ///
+ /// Point to divide.
+ /// Number to divide point with.
+ /// A new instance.
+ public static Point4d operator /(Point4d point, double scalar) => new Point4d(
+ point.X / scalar,
+ point.Y / scalar,
+ point.Z / scalar,
+ point.Weight / scalar);
+
+
+ ///
+ /// Equality check between two instances.
+ ///
+ /// Point to check equality from.
+ /// Point to check equality to.
+ /// True if points are equal.
+ public static bool operator ==(Point4d point, Point4d point2) =>
+ point?.Equals(point2) ?? false;
+
+
+ ///
+ /// Inequality check between two instances.
+ ///
+ /// Point to check inequality from.
+ /// Point to check inequality to.
+ /// True if points are NOT equal.
+ public static bool operator !=(Point4d point, Point4d point2) =>
+ !point?.Equals(point2) ?? true;
+
+
+ ///
+ /// Adds a vector and a point.
+ ///
+ /// A 4d point
+ /// A 3d vector
+ /// A new instance with the resulting coordinates.
+ public static Point4d operator +(Point4d point, Vector3d v) => new Point4d(
+ point.X + v.X,
+ point.Y + v.Y,
+ point.Z + v.Z,
+ point.Weight);
- ///
- public static Point4d operator +(Point4d point, Vector3d v) => new Point4d(point.X + v.X, point.Y + v.Y, point.Z + v.Z, point.Weight);
///
public override bool Equals(object obj)
{
if (obj is Point4d pt)
+ {
return base.Equals(obj)
&& Math.Abs(this.Weight - pt.Weight) < Settings.Tolerance;
+ }
+
return false;
}
+
///
public override int GetHashCode() =>
// TODO: Non consistent getHashCode implementation
base.GetHashCode() ^ this.weight.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 83f4673..e1d097d 100644
--- a/src/Geometry/3D/Polyline.cs
+++ b/src/Geometry/3D/Polyline.cs
@@ -16,6 +16,7 @@ public class Polyline : BaseCurve, IEnumerable
private List segments;
private bool segmentsNeedUpdate;
+
///
/// Initializes a new instance of the class.
///
@@ -26,6 +27,7 @@ public Polyline()
this.segmentsNeedUpdate = false;
}
+
///
/// Initializes a new instance of the class from a list of points.
///
@@ -37,6 +39,7 @@ public Polyline(List knots)
this.RebuildSegments();
}
+
///
/// Gets the segment lines of the polyline.
///
@@ -60,10 +63,7 @@ public List Segments
///
/// Gets the list of knots for this polyline.
///
- public List Knots
- {
- get;
- }
+ public List Knots { get; }
///
/// Gets a value indicating whether the polyline is closed (first point == last point).
@@ -75,11 +75,16 @@ public List Knots
///
public bool IsUnset => this.Knots.Count == 0;
+
///
- public IEnumerator GetEnumerator() => ((IEnumerable)this.Knots).GetEnumerator();
+ public IEnumerator GetEnumerator() =>
+ (( IEnumerable ) this.Knots).GetEnumerator();
+
///
- IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.Knots).GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() =>
+ (( IEnumerable ) this.Knots).GetEnumerator();
+
///
/// Add a new knot vertex at the end of the polyline.
@@ -91,6 +96,7 @@ public void AddKnot(Point3d knot)
this.segmentsNeedUpdate = true;
}
+
///
/// Add a new knot vertex at the specified index.
///
@@ -102,6 +108,7 @@ public void InsertKnot(Point3d knot, int index)
this.segmentsNeedUpdate = true;
}
+
///
/// Delete a specific knot if it exists in the polyline.
/// If the point exists multiple times, it will remove the first occurrence.
@@ -115,6 +122,7 @@ public void RemoveKnot(Point3d knot)
this.segmentsNeedUpdate = true;
}
+
///
/// Delete a knot at a specific index.
///
@@ -129,6 +137,7 @@ public void RemoveKnotAt(int index)
this.segmentsNeedUpdate = true;
}
+
private void RebuildSegments()
{
this.segments = new List(this.Knots.Count - 1);
@@ -144,30 +153,45 @@ private void RebuildSegments()
}
}
- ///
- 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) => (from segment in this.segments
- where segment.Domain.Contains(t)
- select segment.NormalAt(t)).FirstOrDefault();
+ public override Vector3d BinormalAt(double t) => (
+ from segment in this.segments
+ where segment.Domain.Contains(t)
+ select segment.BinormalAt(t))
+ .FirstOrDefault();
+
///
- public override Point3d PointAt(double t) => (from segment in this.segments
- where segment.Domain.Contains(t)
- select segment.PointAt(t)).FirstOrDefault();
+ public override Vector3d NormalAt(double t) => (
+ from segment in this.segments
+ where segment.Domain.Contains(t)
+ select segment.NormalAt(t))
+ .FirstOrDefault();
+
///
- public override Vector3d TangentAt(double t) => (from segment in this.segments
+ public override Point3d PointAt(double t) => (
+ from segment in this.segments
where segment.Domain.Contains(t)
- select segment.TangentAt(t)).FirstOrDefault();
+ select segment.PointAt(t))
+ .FirstOrDefault();
+
///
- public override Plane FrameAt(double t) => (from segment in this.segments
- where segment.Domain.Contains(t)
- select segment.FrameAt(t)).FirstOrDefault();
+ 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) => (
+ from segment in this.segments
+ where segment.Domain.Contains(t)
+ select segment.FrameAt(t)).FirstOrDefault();
+
///
protected override double ComputeLength()
@@ -177,8 +201,10 @@ protected override double ComputeLength()
return length;
}
+
///
- /// Checks the validity of the polyline. Currently only checks if some segments are collapsed (length == 0).
+ /// 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()
diff --git a/src/Geometry/3D/Primitives/Box.cs b/src/Geometry/3D/Primitives/Box.cs
index 6fe43ac..5fe3ae8 100644
--- a/src/Geometry/3D/Primitives/Box.cs
+++ b/src/Geometry/3D/Primitives/Box.cs
@@ -22,8 +22,10 @@ public Box(Plane plane, Interval domainX, Interval domainY, Interval domainZ)
this.DomainZ = domainZ;
}
+
///
- /// Initializes a new instance of the class from 2 corners. Both corners will form the diagonal of
+ /// Initializes a new instance of the class from 2 corners. Both corners will
+ /// form the diagonal of
/// the box.
///
/// Lower left corner point.
@@ -36,51 +38,39 @@ public Box(Point3d lower, Point3d upper)
this.DomainZ = new Interval(lower.Z, upper.Z);
}
+
///
/// Gets or sets the box's base plane.
///
/// .
- public Plane Plane
- {
- get;
- set;
- }
+ public Plane Plane { get; set; }
///
/// Gets or sets the box's X axis domain.
///
/// .
- public Interval DomainX
- {
- get;
- set;
- }
+ public Interval DomainX { get; set; }
///
/// Gets or sets the box's Y axis domain.
///
/// .
- public Interval DomainY
- {
- get;
- set;
- }
+ public Interval DomainY { get; set; }
///
/// Gets or sets the box's Z axis domain.
///
/// .
- public Interval DomainZ
- {
- get;
- set;
- }
+ public Interval DomainZ { get; set; }
///
/// Gets the corner point with lowest values.
///
/// .
- public Point3d Min => new Point3d(this.DomainX.Start, this.DomainY.Start, this.DomainZ.Start);
+ public Point3d Min => new Point3d(
+ this.DomainX.Start,
+ this.DomainY.Start,
+ this.DomainZ.Start);
///
/// Gets the corner point with highest values.
@@ -92,6 +82,9 @@ public Interval DomainZ
/// Gets the center point of the box.
///
/// .
- public Point3d Center => new Point3d(this.DomainX.RemapFromUnit(0.5), this.DomainY.RemapFromUnit(0.5), this.DomainZ.RemapFromUnit(0.5));
+ public Point3d Center => new Point3d(
+ this.DomainX.RemapFromUnit(0.5),
+ this.DomainY.RemapFromUnit(0.5),
+ this.DomainZ.RemapFromUnit(0.5));
}
}
\ No newline at end of file
diff --git a/src/Geometry/3D/Primitives/Cylinder.cs b/src/Geometry/3D/Primitives/Cylinder.cs
index 1eb00b3..4856d0b 100644
--- a/src/Geometry/3D/Primitives/Cylinder.cs
+++ b/src/Geometry/3D/Primitives/Cylinder.cs
@@ -10,7 +10,8 @@ namespace Paramdigma.Core.Geometry
public class Cylinder : ISurface
{
///
- /// Initializes a new instance of the class from it's individual components.
+ /// Initializes a new instance of the class from it's individual
+ /// components.
///
/// The plane of the cylinder.
/// The radius of the cylinder.
@@ -31,35 +32,24 @@ public Cylinder(Plane plane, double radius, Interval domain)
this.DomainV = Interval.Unit;
}
+
///
/// Gets or sets the base plane of the cylinder.
///
/// .
- public Plane Plane
- {
- get;
- set;
- }
+ public Plane Plane { get; set; }
///
/// Gets or sets the radius of the cylinder.
///
/// .
- public double Radius
- {
- get;
- set;
- }
+ public double Radius { get; set; }
///
/// Gets or sets the height range of the cylinder.
///
/// .
- public Interval HeightRange
- {
- get;
- set;
- }
+ public Interval HeightRange { get; set; }
///
/// Gets the cylinder height.
@@ -67,18 +57,11 @@ public Interval HeightRange
public double Height => this.HeightRange.Length;
///
- public Interval DomainU
- {
- get;
- set;
- }
+ public Interval DomainU { get; set; }
///
- public Interval DomainV
- {
- get;
- set;
- }
+ public Interval DomainV { get; set; }
+
///
public Plane FrameAt(double u, double v)
@@ -87,6 +70,7 @@ public Plane FrameAt(double u, double v)
throw new NotImplementedException();
}
+
///
/// Compute the distance from a point to this cylinder.
///
@@ -94,6 +78,7 @@ public Plane FrameAt(double u, double v)
/// Number representing the distance.
public double DistanceTo(Point3d point) => throw new NotImplementedException();
+
///
/// Compute the closes point of a point in this cylinder.
///
@@ -101,6 +86,7 @@ public Plane FrameAt(double u, double v)
/// Point3d instance of the closest point in the cylinder.
public Point3d ClosestPointTo(Point3d point) => throw new NotImplementedException();
+
///
public Vector3d NormalAt(double u, double v)
{
@@ -108,6 +94,7 @@ public Vector3d NormalAt(double u, double v)
throw new NotImplementedException();
}
+
///
public Point3d PointAt(double u, double v)
{
@@ -122,6 +109,7 @@ public Point3d PointAt(double u, double v)
return this.Plane.PointAt(x, y, z);
}
+
private void CheckParameters(double u, double v)
{
if (!this.DomainU.Contains(u))
diff --git a/src/Geometry/3D/Primitives/Sphere.cs b/src/Geometry/3D/Primitives/Sphere.cs
index 7af9a30..6447590 100644
--- a/src/Geometry/3D/Primitives/Sphere.cs
+++ b/src/Geometry/3D/Primitives/Sphere.cs
@@ -9,6 +9,12 @@ namespace Paramdigma.Core.Geometry
///
public class Sphere : ISurface
{
+ ///
+ /// Initializes a new instance of given it's base plane and radius.
+ ///
+ ///
+ ///
+ /// Throws when radius is smaller than 0.
public Sphere(Plane plane, double radius)
{
if (Math.Abs(radius) < Settings.Tolerance)
@@ -19,53 +25,49 @@ public Sphere(Plane plane, double radius)
this.DomainV = Interval.Unit;
}
+
+ ///
+ /// Initializes a new instance of around the World origin with unit radius.
+ ///
public Sphere() : this(Plane.WorldXY, 1) { }
+
///
/// Gets or sets the base plane of the sphere.
///
/// .
- public Plane Plane
- {
- get;
- set;
- }
+ public Plane Plane { get; set; }
///
/// Gets or sets the radius of the sphere.
///
/// .
- public double Radius
- {
- get;
- set;
- }
+ public double Radius { get; set; }
///
- public Interval DomainU
- {
- get;
- set;
- }
+ public Interval DomainU { get; set; }
///
- public Interval DomainV
- {
- get;
- set;
- }
+ public Interval DomainV { get; set; }
+
///
- public double DistanceTo(Point3d point) => this.Plane.Origin.DistanceTo(point) - this.Radius;
+ public double DistanceTo(Point3d point) =>
+ this.Plane.Origin.DistanceTo(point) - this.Radius;
+
///
- public Point3d ClosestPointTo(Point3d point) => this.Plane.Origin + ((point - this.Plane.Origin).Unit() * this.Radius);
+ public Point3d ClosestPointTo(Point3d point) =>
+ this.Plane.Origin + (point - this.Plane.Origin).Unit() * this.Radius;
+
///
public Plane FrameAt(double u, double v) => throw new NotImplementedException();
+
///
- public Vector3d NormalAt(double u, double v) => (this.PointAt(u, v) - this.Plane.Origin).Unit();
+ public Vector3d NormalAt(double u, double v) =>
+ (this.PointAt(u, v) - this.Plane.Origin).Unit();
///
@@ -80,6 +82,7 @@ public Point3d PointAt(double u, double v)
return this.Plane.PointAt(x, y, z);
}
+
///
/// Returns the closest point on the sphere as a 2D point containing it's UV coordinates.
///
@@ -88,12 +91,13 @@ public Point3d PointAt(double u, double v)
public Point2d ClosestParam(Point3d pt)
{
var rho = Math.Atan(pt.Y / pt.X);
- var tau = Math.Atan(Math.Sqrt((pt.X * pt.X) + (pt.Y * pt.Y)) / pt.Z);
+ var tau = Math.Atan(Math.Sqrt(pt.X * pt.X + pt.Y * pt.Y) / pt.Z);
var u = new Interval(0, 2 * Math.PI).RemapToUnit(rho);
var v = new Interval(0, Math.PI).RemapToUnit(tau);
return new Point2d(u, v);
}
+
///
/// Computes the point at a specified parameter, provided as a instance.
///
diff --git a/src/Geometry/3D/Primitives/Torus.cs b/src/Geometry/3D/Primitives/Torus.cs
index 1d85855..6f0eeb6 100644
--- a/src/Geometry/3D/Primitives/Torus.cs
+++ b/src/Geometry/3D/Primitives/Torus.cs
@@ -22,62 +22,48 @@ public Torus(Plane plane, double majorRadius, double minorRadius)
this.MinorRadius = minorRadius;
}
+
///
/// Gets or sets the torus base plane.
///
/// .
- public Plane Plane
- {
- get;
- set;
- }
+ public Plane Plane { get; set; }
///
/// Gets or sets the torus major radius.
///
/// .
- public double MajorRadius
- {
- get;
- set;
- }
+ public double MajorRadius { get; set; }
///
/// Gets or sets the torus minor radius.
///
/// .
- public double MinorRadius
- {
- get;
- set;
- }
+ public double MinorRadius { get; set; }
///
- public Interval DomainU
- {
- get;
- set;
- }
+ public Interval DomainU { get; set; }
///
- public Interval DomainV
- {
- get;
- set;
- }
+ public Interval DomainV { get; set; }
+
///
public Plane FrameAt(double u, double v) => throw new NotImplementedException();
+
///
public double DistanceTo(Point3d point) => throw new NotImplementedException();
+
///
public Point3d ClosestPointTo(Point3d point) => throw new NotImplementedException();
+
///
public Vector3d NormalAt(double u, double v) => throw new NotImplementedException();
+
///
public Point3d PointAt(double u, double v) => throw new NotImplementedException();
}
diff --git a/src/Geometry/3D/Ray.cs b/src/Geometry/3D/Ray.cs
index e9cf317..d107daa 100644
--- a/src/Geometry/3D/Ray.cs
+++ b/src/Geometry/3D/Ray.cs
@@ -18,29 +18,23 @@ public Ray(Point3d origin, Vector3d direction)
this.Direction = direction ?? throw new ArgumentNullException(nameof(direction));
}
+
///
/// Gets or sets the origin point of the ray.
///
- public Point3d Origin
- {
- get;
- set;
- }
+ public Point3d Origin { get; set; }
///
/// Gets or sets the direction vector of the ray.
///
- public Vector3d Direction
- {
- get;
- set;
- }
+ public Vector3d Direction { get; set; }
+
///
/// Computes a point in the ray at the given parameter.
///
/// Parameter to obtain point.
/// Returns a point at the specified parameter of the Ray.
- public Point3d PointAt(double t) => this.Origin + (t * this.Direction);
+ public Point3d PointAt(double t) => this.Origin + t * this.Direction;
}
}
\ No newline at end of file
diff --git a/src/Geometry/3D/Vector3d.cs b/src/Geometry/3D/Vector3d.cs
index 2a41455..f7f455b 100644
--- a/src/Geometry/3D/Vector3d.cs
+++ b/src/Geometry/3D/Vector3d.cs
@@ -12,13 +12,16 @@ public class Vector3d : BasePoint
///
public Vector3d() { }
+
///
- /// Initializes a new instance of the class with the same values as the provided vector.
+ /// Initializes a new instance of the class with the same values as the
+ /// provided vector.
///
/// Vector to copy values from.
public Vector3d(Vector3d vector)
: base(vector) { }
+
///
/// Initializes a new instance of the class from a v.
///
@@ -27,6 +30,7 @@ public Vector3d(Vector3d vector)
public Vector3d(Point3d point)
: base(point) { }
+
///
/// Initializes a new instance of the class given it's 3 coordinates.
///
@@ -37,6 +41,7 @@ public Vector3d(Point3d point)
public Vector3d(double xCoord, double yCoord, double zCoord)
: base(xCoord, yCoord, zCoord) { }
+
///
/// Gets the Euclidean length squared of this vector.
///
@@ -67,6 +72,7 @@ public Vector3d(double xCoord, double yCoord, double zCoord)
/// Vector {0,1,0}.
public static Vector3d UnitZ => new Vector3d(0, 0, 1);
+
///
/// Divides this vector by it's euclidean length.
///
@@ -78,6 +84,7 @@ public void Unitize()
this.Z /= length;
}
+
///
/// Returns a normalized copy of this vector.
///
@@ -91,6 +98,7 @@ public Vector3d Unit()
return new Vector3d(x, y, z);
}
+
///
/// Computes the dot product of this vector and v.
///
@@ -98,6 +106,7 @@ public Vector3d Unit()
/// Dot product.
public double Dot(Vector3d v) => DotProduct(this, v);
+
///
/// Returns the cross product of this vector and v.
///
@@ -105,6 +114,7 @@ public Vector3d Unit()
/// Cross product vector.
public Vector3d Cross(Vector3d v) => CrossProduct(this, v);
+
///
/// Gets the scalar product (dot product) of two given vectors
/// Dot product = u1*v1 + u2*v2 + u3*v3.
@@ -112,7 +122,9 @@ public Vector3d Unit()
/// First vector.
/// Second vector.
/// Numerical value of the dot product.
- public static double DotProduct(Vector3d u, Vector3d v) => (u.X * v.X) + (u.Y * v.Y) + (u.Z * v.Z);
+ public static double DotProduct(Vector3d u, Vector3d v) =>
+ u.X * v.X + u.Y * v.Y + u.Z * v.Z;
+
///
/// Computes the vector product (cross product) of two given vectors
@@ -123,13 +135,14 @@ public Vector3d Unit()
/// Vector result of the cross product.
public static Vector3d CrossProduct(Vector3d u, Vector3d v)
{
- var x = (u.Y * v.Z) - (u.Z * v.Y);
- var y = (u.Z * v.X) - (u.X * v.Z);
- var z = (u.X * v.Y) - (u.Y * v.X);
+ var x = u.Y * v.Z - u.Z * v.Y;
+ var y = u.Z * v.X - u.X * v.Z;
+ var z = u.X * v.Y - u.Y * v.X;
return new Vector3d(x, y, z);
}
+
///
/// Computes the angle in Radians between two given vectors
/// Angle = Arcosine of the CrossProduct of UxV divided with their multiplied lengths.
@@ -137,7 +150,9 @@ public static Vector3d CrossProduct(Vector3d u, Vector3d v)
/// First vector.
/// Second vector.
/// Angle formed between u and v.
- public static double Angle(Vector3d u, Vector3d v) => Math.Acos(DotProduct(u, v) / (u.Length * v.Length));
+ public static double Angle(Vector3d u, Vector3d v) =>
+ Math.Acos(DotProduct(u, v) / (u.Length * v.Length));
+
///
/// Adds one vector to another.
@@ -145,7 +160,9 @@ public static Vector3d CrossProduct(Vector3d u, Vector3d v)
/// First vector to add.
/// Second vector to add.
/// New vector entity with the result of the addition.
- public static Vector3d operator +(Vector3d v, Vector3d v2) => new Vector3d(v.X + v2.X, v.Y + v2.Y, v.Z + v2.Z);
+ public static Vector3d operator +(Vector3d v, Vector3d v2) =>
+ new Vector3d(v.X + v2.X, v.Y + v2.Y, v.Z + v2.Z);
+
///
/// Substracts one vector from another.
@@ -153,7 +170,9 @@ public static Vector3d CrossProduct(Vector3d u, Vector3d v)
/// Vector to substract from.
/// Vector to be substracted.
/// New vector entity with the result of the substraction.
- public static Vector3d operator -(Vector3d v, Vector3d v2) => new Vector3d(v.X - v2.X, v.Y - v2.Y, v.Z - v2.Z);
+ public static Vector3d operator -(Vector3d v, Vector3d v2) =>
+ new Vector3d(v.X - v2.X, v.Y - v2.Y, v.Z - v2.Z);
+
///
/// Multiply one vector by a number.
@@ -161,7 +180,9 @@ public static Vector3d CrossProduct(Vector3d u, Vector3d v)
/// Vector to multiply.
/// Number to multiply with.
/// New vector entity with the result of the multiplication.
- public static Vector3d operator *(Vector3d v, double scalar) => new Vector3d(v.X * scalar, v.Y * scalar, v.Z * scalar);
+ public static Vector3d operator *(Vector3d v, double scalar) =>
+ new Vector3d(v.X * scalar, v.Y * scalar, v.Z * scalar);
+
///
/// Multiply one vector by a number.
@@ -169,7 +190,9 @@ public static Vector3d CrossProduct(Vector3d u, Vector3d v)
/// Number to multiply with.
/// Vector to multiply.
/// New vector entity with the result of the multiplication.
- public static Vector3d operator *(double scalar, Vector3d v) => new Vector3d(v.X * scalar, v.Y * scalar, v.Z * scalar);
+ public static Vector3d operator *(double scalar, Vector3d v) =>
+ new Vector3d(v.X * scalar, v.Y * scalar, v.Z * scalar);
+
///
/// Negate a vector.
@@ -182,13 +205,16 @@ public static Vector3d CrossProduct(Vector3d u, Vector3d v)
v.Y != 0 ? -v.Y : 0,
v.Z != 0 ? -v.Z : 0);
+
///
/// Divide a vector by a number.
///
/// Vector to be divided.
/// Number to be divided by.
/// New vector entity with the result of the division.
- public static Vector3d operator /(Vector3d v, double scalar) => new Vector3d(v.X / scalar, v.Y / scalar, v.Z / scalar);
+ public static Vector3d operator /(Vector3d v, double scalar) =>
+ new Vector3d(v.X / scalar, v.Y / scalar, v.Z / scalar);
+
///
/// Checks if two vectors are equal.
@@ -196,7 +222,8 @@ public static Vector3d CrossProduct(Vector3d u, Vector3d v)
/// First vector.
/// Second vector.
/// Result of the comparison between v and w.
- public static bool operator ==(Vector3d v, Vector3d w) => v.Equals(w);
+ public static bool operator ==(Vector3d v, Vector3d w) => v?.Equals(w) ?? false;
+
///
/// Checks if two vectors are not equal.
@@ -204,17 +231,20 @@ public static Vector3d CrossProduct(Vector3d u, Vector3d v)
/// First vector.
/// Second vector.
/// Result of the comparison between v and w.
- public static bool operator !=(Vector3d v, Vector3d w) => !v.Equals(w);
+ public static bool operator !=(Vector3d v, Vector3d w) => !v?.Equals(w) ?? true;
+
///
public override bool Equals(object obj) => base.Equals(obj);
+
///
/// Converts a vector into a string.
///
/// Returns a string representation of this vector.
public override string ToString() => "Vector3d" + base.ToString();
+
///
public override int GetHashCode() => base.GetHashCode();
}
diff --git a/src/Geometry/3D/VectorNd.cs b/src/Geometry/3D/VectorNd.cs
index af6ac18..3da93ed 100644
--- a/src/Geometry/3D/VectorNd.cs
+++ b/src/Geometry/3D/VectorNd.cs
@@ -12,12 +12,14 @@ public class VectorNd : IEnumerable, IEquatable
{
private List values;
+
///
/// Initializes a new instance of the class.
///
/// Vector to copy.
public VectorNd(VectorNd vector) => this.values = new List(vector);
+
///
/// Initializes a new instance of the class.
/// Constructs a zero vector of a given dimension.
@@ -25,18 +27,23 @@ public class VectorNd : IEnumerable, IEquatable
/// Dimension.
public VectorNd(int dimension) => this.InitializeZeroVector(dimension);
+
///
- /// Initializes a new instance of the class from a given list of coordinates.
+ /// Initializes a new instance of the class from a given list of
+ /// coordinates.
///
/// List of values for the vector.
public VectorNd(List values) => this.values = values;
+
///
- /// Initializes a new instance of the class from a given list of parameters.
+ /// Initializes a new instance of the class from a given list of
+ /// parameters.
///
/// Number parameters.
public VectorNd(params double[] values) : this(values.ToList()) { }
+
///
/// Gets or sets the coordinate at the given dimension.
///
@@ -62,11 +69,14 @@ public double this[int dimension]
///
public double Length2 => this.ComputeLength2();
+
///
public IEnumerator GetEnumerator() => this.values.GetEnumerator();
+
IEnumerator IEnumerable.GetEnumerator() => this.values.GetEnumerator();
+
///
/// Compare this vector with another.
///
@@ -74,6 +84,9 @@ public double this[int dimension]
/// Comparison result.
public bool Equals(VectorNd vector)
{
+ if (vector == null)
+ return false;
+
var max = Math.Max(this.Dimension, vector.Dimension);
for (var index = 0; index < max; index++)
{
@@ -87,6 +100,7 @@ public bool Equals(VectorNd vector)
return true;
}
+
///
/// Gets the dot product of this vector with another.
///
@@ -94,6 +108,7 @@ public bool Equals(VectorNd vector)
/// Dot product result.
public double Dot(VectorNd vector) => DotProduct(this, vector);
+
private double ComputeLength2()
{
var length = .0;
@@ -101,6 +116,7 @@ private double ComputeLength2()
return length;
}
+
private void InitializeZeroVector(int dimension)
{
this.values = new List(dimension);
@@ -108,6 +124,7 @@ private void InitializeZeroVector(int dimension)
this.values.Add(.0);
}
+
///
/// Computes the distance between two N-dimensional vectors.
///
@@ -116,6 +133,7 @@ private void InitializeZeroVector(int dimension)
/// Distance between vectors.
public static double Distance(VectorNd a, VectorNd b) => Math.Sqrt(Distance2(a, b));
+
///
/// Computes the square distance between two N-dimensional vectors.
///
@@ -135,13 +153,16 @@ public static double Distance2(VectorNd a, VectorNd b)
return dist;
}
+
///
/// Computes the cosine similarity between two vectors.
///
/// Vector A.
/// Vector B.
/// Cosine similarity value.
- public static double CosineSimilarity(VectorNd a, VectorNd b) => a.Dot(b) / (a.Length * b.Length);
+ public static double CosineSimilarity(VectorNd a, VectorNd b) =>
+ a.Dot(b) / (a.Length * b.Length);
+
///
/// Computes the angular distance between two vectors.
@@ -149,7 +170,9 @@ public static double Distance2(VectorNd a, VectorNd b)
/// Vector A.
/// Vector B.
/// Angular distance value.
- public static double AngularDistance(VectorNd a, VectorNd b) => Math.Acos(CosineSimilarity(a, b)) / Math.PI;
+ public static double AngularDistance(VectorNd a, VectorNd b) =>
+ Math.Acos(CosineSimilarity(a, b)) / Math.PI;
+
///
/// Angular similarity value between two vectors.
@@ -159,6 +182,7 @@ public static double Distance2(VectorNd a, VectorNd b)
/// Angular similarity value.
public static double AngularSimilarity(VectorNd a, VectorNd b) => 1 - AngularDistance(a, b);
+
///
/// Adds two N dimensional vectors.
///
@@ -179,6 +203,7 @@ public static VectorNd Add(VectorNd a, VectorNd b)
return result;
}
+
///
/// Subtract two N dimensional vectors.
///
@@ -187,6 +212,7 @@ public static VectorNd Add(VectorNd a, VectorNd b)
/// Vector substraction result.
public static VectorNd Substract(VectorNd a, VectorNd b) => Add(a, Negate(b));
+
///
/// Multiply an N dimensional vector by a number.
///
@@ -201,13 +227,16 @@ public static VectorNd Multiply(VectorNd vector, double scalar)
return result;
}
+
///
/// Divide an N dimensional vector by a number.
///
/// Vector to divide.
/// Number to divide by.
/// Vector division result.
- public static VectorNd Divide(VectorNd vector, double scalar) => Multiply(vector, 1 / scalar);
+ public static VectorNd Divide(VectorNd vector, double scalar) =>
+ Multiply(vector, 1 / scalar);
+
///
/// Negate a given vector.
@@ -222,6 +251,7 @@ public static VectorNd Negate(VectorNd vector)
return result;
}
+
///
/// Computes the dot product between two vectors.
///
@@ -242,18 +272,22 @@ public static double DotProduct(VectorNd vectorA, VectorNd vectorB)
return result;
}
- ///
- public static VectorNd operator +(VectorNd vectorA, VectorNd vectorB) => Add(vectorA, vectorB);
- ///
- public static VectorNd operator -(VectorNd vectorA, VectorNd vectorB) => Substract(vectorA, vectorB);
+ public static VectorNd operator +(VectorNd vectorA, VectorNd vectorB) =>
+ Add(vectorA, vectorB);
+
+
+ public static VectorNd operator -(VectorNd vectorA, VectorNd vectorB) =>
+ Substract(vectorA, vectorB);
+
+
+ public static VectorNd operator *(VectorNd vector, double scalar) =>
+ Multiply(vector, scalar);
- ///
- public static VectorNd operator *(VectorNd vector, double scalar) => Multiply(vector, scalar);
- ///
public static VectorNd operator /(VectorNd vector, double scalar) => Divide(vector, scalar);
+
///
/// Compare this vector with another object.
///
@@ -267,20 +301,21 @@ public override bool Equals(object obj)
return false;
}
+
///
public override int GetHashCode()
{
unchecked
{
// Choose large primes to avoid hashing collisions
- const int hashingBase = (int)2166136261;
+ const int hashingBase = ( int ) 2166136261;
const int hashingMultiplier = 16777619;
var tol = Settings.Tolerance * 2;
var hash = hashingBase;
foreach (var coord in this.values)
{
- var tCoord = (int)(coord * (1 / tol)) * tol; // Round to tolerance
+ var tCoord = ( int ) (coord * (1 / tol)) * tol; // Round to tolerance
hash = (hash * hashingMultiplier) ^ tCoord.GetHashCode();
}
@@ -288,7 +323,9 @@ public override int GetHashCode()
}
}
+
///
- public override string ToString() => "VectorNd[" + this[0] + "," + this[1] + "," + this[2] + ",...]";
+ public override string ToString() =>
+ "VectorNd[" + this[0] + "," + this[1] + "," + this[2] + ",...]";
}
}
\ No newline at end of file
diff --git a/src/Geometry/Base/BaseCurve.cs b/src/Geometry/Base/BaseCurve.cs
index 00b18bb..15ab134 100644
--- a/src/Geometry/Base/BaseCurve.cs
+++ b/src/Geometry/Base/BaseCurve.cs
@@ -5,7 +5,8 @@
namespace Paramdigma.Core.Geometry
{
///
- /// Represents a generic curve. This class is abstract and all curve classes should inherit from it.
+ /// Represents a generic curve. This class is abstract and all curve classes should inherit from
+ /// it.
///
public abstract class BaseCurve
{
@@ -15,14 +16,11 @@ public abstract class BaseCurve
protected BaseCurve() => this.Domain = Interval.Unit;
// Public properties
+
///
/// Gets or sets the curve's domain.
///
- public Interval Domain
- {
- get;
- set;
- }
+ public Interval Domain { get; set; }
///
/// Gets a value indicating whether the curve is valid.
@@ -32,6 +30,7 @@ public Interval Domain
public double Length => this.ComputeLength();
+
///
/// Compute a point along the curve at a specified parameter.
///
@@ -39,6 +38,7 @@ public Interval Domain
/// Point at the parameter specified.
public abstract Point3d PointAt(double t);
+
///
/// Compute the tangent vector along the curve at a specified parameter.
///
@@ -46,6 +46,7 @@ public Interval Domain
/// Tangent vector at the parameter specified.
public abstract Vector3d TangentAt(double t);
+
///
/// Compute normal vector along the curve at a specified parameter.
///
@@ -53,6 +54,7 @@ public Interval Domain
/// Normal vector at the parameter specified.
public abstract Vector3d NormalAt(double t);
+
///
/// Compute a binormal vector along the curve at a specified parameter.
///
@@ -60,6 +62,7 @@ public Interval Domain
/// Binormal vector at the parameter specified.
public abstract Vector3d BinormalAt(double t);
+
///
/// Compute the perpendicular frame along the curve at a specified parameter.
///
@@ -67,12 +70,14 @@ public Interval Domain
/// Perpendicular plane at the parameter specified.
public abstract Plane FrameAt(double t);
+
///
/// Checks the validity of the curve.
///
/// True if valid.
public abstract bool CheckValidity();
+
///
/// Computes the length of the curve.
///
diff --git a/src/Geometry/Base/BasePoint.cs b/src/Geometry/Base/BasePoint.cs
index 40ce1da..9897cf1 100644
--- a/src/Geometry/Base/BasePoint.cs
+++ b/src/Geometry/Base/BasePoint.cs
@@ -13,6 +13,7 @@ public abstract class BasePoint
private double y;
private double z;
+
///
/// Initializes a new instance of the class.
///
@@ -21,6 +22,7 @@ protected BasePoint()
: this(0, 0, 0) =>
this.IsUnset = true;
+
///
/// Initializes a new instance of the class.
///
@@ -29,6 +31,7 @@ protected BasePoint()
protected BasePoint(BasePoint point)
: this(point.X, point.Y, point.Z) { }
+
///
/// Initializes a new instance of the class by cartesian coordinates.
///
@@ -43,6 +46,7 @@ protected BasePoint(double xCoord, double yCoord, double zCoord)
this.IsUnset = false;
}
+
///
/// Gets or sets x Coordinate.
///
@@ -92,11 +96,8 @@ 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; }
+
///
/// Add a point to this point.
@@ -110,6 +111,7 @@ public void Add(BasePoint point)
this.IsUnset = false;
}
+
///
/// Substract a point from this one.
///
@@ -122,6 +124,7 @@ public void Substract(BasePoint point)
this.IsUnset = false;
}
+
///
/// Multiply this point by a number.
///
@@ -133,6 +136,7 @@ public void Multiply(double scalar)
this.z *= scalar;
}
+
///
/// Divide this point by a number.
///
@@ -144,6 +148,7 @@ public void Divide(double scalar)
this.z /= scalar;
}
+
///
/// Negates this point.
///
@@ -154,18 +159,21 @@ public void Negate()
this.z = this.z != 0 ? -this.z : 0;
}
+
///
/// Returns the string representation of this point.
///
///
public override string ToString() => "{ " + this.x + ", " + this.y + ", " + this.z + " }";
+
///
/// Converts a point to an array of numbers.
///
/// Array with cartesian coordinates of point.
public double[] ToArray() => new[] {this.x, this.y, this.z};
+
///
/// Performs a deep clone of the point.
///
@@ -177,24 +185,25 @@ public override bool Equals(object obj)
if (!(obj is BasePoint))
return false;
- var pt = (BasePoint)obj;
+ var pt = ( BasePoint ) obj;
return Math.Abs(this.X - pt.X) <= Settings.Tolerance
&& Math.Abs(this.Y - pt.Y) <= Settings.Tolerance
&& Math.Abs(this.Z - pt.Z) <= Settings.Tolerance;
}
+
///
public override int GetHashCode()
{
unchecked
{
// Choose large primes to avoid hashing collisions
- const int hashingBase = (int)2166136261;
+ const int hashingBase = ( int ) 2166136261;
const int hashingMultiplier = 16777619;
var tol = Settings.Tolerance * 2;
- var tX = (int)(this.X * (1 / tol)) * tol;
- var tY = (int)(this.Y * (1 / tol)) * tol;
- var tZ = (int)(this.Z * (1 / tol)) * tol;
+ var tX = ( int ) (this.X * (1 / tol)) * tol;
+ var tY = ( int ) (this.Y * (1 / tol)) * tol;
+ var tZ = ( int ) (this.Z * (1 / tol)) * tol;
var hash = hashingBase;
hash = (hash * hashingMultiplier) ^ tX.GetHashCode();
diff --git a/src/Geometry/Base/InvalidCurveException.cs b/src/Geometry/Base/InvalidCurveException.cs
index a9f86ae..5c74ebc 100644
--- a/src/Geometry/Base/InvalidCurveException.cs
+++ b/src/Geometry/Base/InvalidCurveException.cs
@@ -10,10 +10,12 @@ public class InvalidCurveException : Exception
///
public InvalidCurveException() { }
+
///
public InvalidCurveException(string message)
: base(message) { }
+
///
public InvalidCurveException(string message, Exception innerException)
: base(message, innerException) { }
diff --git a/src/Geometry/Interfaces/ICurve.cs b/src/Geometry/Interfaces/ICurve.cs
index e6e809e..ff1690a 100644
--- a/src/Geometry/Interfaces/ICurve.cs
+++ b/src/Geometry/Interfaces/ICurve.cs
@@ -12,6 +12,7 @@ public interface ICurve
/// Point on curve.
Point3d PointAt(double t);
+
///
/// Computes the tangent vector on the curve at the specified parameter.
///
@@ -19,6 +20,7 @@ public interface ICurve
/// Tangent on curve.
Vector3d TangentAt(double t);
+
///
/// Computes the normal vector on the curve at the specified parameter.
///
@@ -26,6 +28,7 @@ public interface ICurve
/// Normal on curve.
Vector3d NormalAt(double t);
+
///
/// Computes the binormal vector on the curve at the specified parameter.
///
@@ -33,6 +36,7 @@ public interface ICurve
/// Binormal vector on curve.
Vector3d BinormalAt(double t);
+
///
/// Computes the perpendicular frame on the curve at the specified parameter.
///
diff --git a/src/Geometry/Interfaces/ISurface.cs b/src/Geometry/Interfaces/ISurface.cs
index 9809989..e7bb22c 100644
--- a/src/Geometry/Interfaces/ISurface.cs
+++ b/src/Geometry/Interfaces/ISurface.cs
@@ -11,19 +11,14 @@ 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.
@@ -33,6 +28,7 @@ Interval DomainV
/// .
Point3d PointAt(double u, double v);
+
///
/// Compute the normal at the specified surface coordinates.
///
@@ -41,6 +37,7 @@ Interval DomainV
/// Normal vector.
Vector3d NormalAt(double u, double v);
+
///
/// Compute the tangent plane at the specified surface coordinates.
///
@@ -49,6 +46,7 @@ Interval DomainV
/// Tangent plane.
Plane FrameAt(double u, double v);
+
///
/// Compute the distance between this surface and a point.
///
@@ -56,6 +54,7 @@ Interval DomainV
/// Number representing the distance.
double DistanceTo(Point3d point);
+
///
/// Compute the projection of a point on this surface.
///
diff --git a/src/Geometry/Intersect/Intersect.cs b/src/Geometry/Intersect/Intersect.cs
index c494794..42e40d0 100644
--- a/src/Geometry/Intersect/Intersect.cs
+++ b/src/Geometry/Intersect/Intersect.cs
@@ -18,7 +18,10 @@ public static partial class Intersect3D
/// The 3d plane to intersect.
/// The resulting intersection point, if it exists.
/// Intersection result.
- public static ISLinePlane LinePlane(Line line, Plane plane, out Point3d intersectionPoint)
+ public static LinePlaneIntersectionStatus LinePlane(
+ Line line,
+ Plane plane,
+ out Point3d intersectionPoint)
{
var u = line.EndPoint - line.StartPoint;
var w = line.StartPoint - plane.Origin;
@@ -33,11 +36,11 @@ public static ISLinePlane LinePlane(Line line, Plane plane, out Point3d intersec
{
// Segment lies in plane
intersectionPoint = null;
- return ISLinePlane.OnPlane;
+ return LinePlaneIntersectionStatus.OnPlane;
}
intersectionPoint = null;
- return ISLinePlane.NoIntersection;
+ return LinePlaneIntersectionStatus.NoIntersection;
}
// They are not parallel
@@ -46,13 +49,14 @@ public static ISLinePlane LinePlane(Line line, Plane plane, out Point3d intersec
if (sI < 0 || sI > 1)
{
intersectionPoint = null;
- return ISLinePlane.NoIntersection;
+ return LinePlaneIntersectionStatus.NoIntersection;
}
- intersectionPoint = line.StartPoint + (u * sI); // Compute segment intersection point
- return ISLinePlane.Point;
+ intersectionPoint = line.StartPoint + u * sI; // Compute segment intersection point
+ return LinePlaneIntersectionStatus.Point;
}
+
///
/// Compute the intersection between a mesh face perimeter and a ray tangent to the face.
///
@@ -61,7 +65,11 @@ public static ISLinePlane LinePlane(Line line, Plane plane, out Point3d intersec
/// The resulting intersection point.
/// The half-edge on where the intersection lies.
/// Intersection result.
- public static ISRayFacePerimeter RayFacePerimeter(Ray ray, MeshFace face, out Point3d result, out MeshHalfEdge halfEdge)
+ public static RayFacePerimeterIntersectionStatus RayFacePerimeter(
+ Ray ray,
+ MeshFace face,
+ out Point3d result,
+ out MeshHalfEdge halfEdge)
{
var faceNormal = MeshGeometry.FaceNormal(face);
var biNormal = Vector3d.CrossProduct(ray.Direction, faceNormal);
@@ -73,55 +81,56 @@ public static ISRayFacePerimeter RayFacePerimeter(Ray ray, MeshFace face, out Po
var temp = new Point3d();
var line = new Line(vertices[0], vertices[1]);
- if (LinePlane(line, perpPlane, out temp) != ISLinePlane.Point)
+ if (LinePlane(line, perpPlane, out temp) != LinePlaneIntersectionStatus.Point)
{
result = null;
halfEdge = null;
- return ISRayFacePerimeter.Point;
+ return RayFacePerimeterIntersectionStatus.Point;
} // No intersection found
if (temp != ray.Origin && temp != null)
{
result = temp;
halfEdge = null;
- return ISRayFacePerimeter.Point;
+ return RayFacePerimeterIntersectionStatus.Point;
} // Intersection found
line = new Line(vertices[1], vertices[2]);
- if (LinePlane(line, perpPlane, out temp) != ISLinePlane.Point)
+ if (LinePlane(line, perpPlane, out temp) != LinePlaneIntersectionStatus.Point)
{
result = null;
halfEdge = null;
- return ISRayFacePerimeter.NoIntersection;
- } // No intersection found
+ return RayFacePerimeterIntersectionStatus.NoIntersection;
+ }
if (temp != ray.Origin && temp != null)
{
result = temp;
halfEdge = null;
- return ISRayFacePerimeter.Point;
- } // Intersection found
+ return RayFacePerimeterIntersectionStatus.Point;
+ }
line = new Line(vertices[2], vertices[0]);
- if (LinePlane(line, perpPlane, out temp) != ISLinePlane.Point)
+ if (LinePlane(line, perpPlane, out temp) != LinePlaneIntersectionStatus.Point)
{
result = null;
halfEdge = null;
- return ISRayFacePerimeter.NoIntersection;
+ return RayFacePerimeterIntersectionStatus.NoIntersection;
}
if (temp != ray.Origin && temp != null)
{
result = temp;
halfEdge = null;
- return ISRayFacePerimeter.Point;
+ return RayFacePerimeterIntersectionStatus.Point;
}
result = null;
halfEdge = null;
- return ISRayFacePerimeter.Error;
+ return RayFacePerimeterIntersectionStatus.Error;
}
+
///
/// Compute the intersection between two 3-dimensional lines.
///
@@ -129,7 +138,10 @@ public static ISRayFacePerimeter RayFacePerimeter(Ray ray, MeshFace face, out Po
/// Second line to intersect.
/// Struct containing the intersection result.
/// Returns an enum containing the intersection status.
- public static ISLineLine LineLine(Line lineA, Line lineB, out IRLineLine result)
+ public static LineLineIntersectionStatus LineLine(
+ Line lineA,
+ Line lineB,
+ out LineLineIntersectionResult result)
{
var u = lineA.EndPoint - lineA.StartPoint;
var v = lineB.EndPoint - lineB.StartPoint;
@@ -139,7 +151,7 @@ public static ISLineLine LineLine(Line lineA, Line lineB, out IRLineLine result)
var c = v.Dot(v); // always >= 0
var d = u.Dot(w);
var e = v.Dot(w);
- var d2 = (a * c) - (b * b); // always >= 0
+ var d2 = a * c - b * b; // always >= 0
double sc, sN, sD = d2; // sc = sN / sD, default sD = D >= 0
double tc, tN, tD = d2; // tc = tN / tD, default tD = D >= 0
@@ -155,8 +167,8 @@ public static ISLineLine LineLine(Line lineA, Line lineB, out IRLineLine result)
else
{
// get the closest points on the infinite lines
- sN = (b * e) - (c * d);
- tN = (a * e) - (b * d);
+ sN = b * e - c * d;
+ tN = a * e - b * d;
if (sN < 0.0)
{
// sc < 0 => the s=0 edge is visible
@@ -211,19 +223,19 @@ public static ISLineLine LineLine(Line lineA, Line lineB, out IRLineLine result)
tc = Math.Abs(tN) < Settings.Tolerance ? 0.0 : tN / tD;
// get the difference of the two closest points
- var dP = w + ((sc * u) - (tc * v)); // = S1(sc) - S2(tc)
+ var dP = w + (sc * u - tc * v); // = S1(sc) - S2(tc)
result = default;
result.Distance = dP.Length; // return the closest distance
- result.TA = sc;
- result.TB = tc;
+ result.ParamA = sc;
+ result.ParamB = tc;
result.PointA = lineA.PointAt(sc);
result.PointB = lineB.PointAt(tc);
if (result.Distance <= Settings.Tolerance)
- return ISLineLine.Point;
+ return LineLineIntersectionStatus.Point;
if (result.Distance > Settings.Tolerance)
- return ISLineLine.NoIntersection;
- return ISLineLine.Error;
+ return LineLineIntersectionStatus.NoIntersection;
+ return LineLineIntersectionStatus.Error;
}
}
}
\ No newline at end of file
diff --git a/src/Geometry/Intersect/IntersectErrors.cs b/src/Geometry/Intersect/IntersectErrors.cs
index d10ae97..a34d280 100644
--- a/src/Geometry/Intersect/IntersectErrors.cs
+++ b/src/Geometry/Intersect/IntersectErrors.cs
@@ -9,54 +9,32 @@ namespace Paramdigma.Core
///
public static partial class Intersect3D
{
- public enum ISLineLine
+ public enum LineLineIntersectionStatus
{
NoIntersection, Point, Error
}
- // INFO: IS prefix stands for Intersection Status
- // INFO: IR prefix stands for Intersection Result
- public enum ISLinePlane
+ public enum LinePlaneIntersectionStatus
{
NoIntersection, Point, OnPlane
}
- public enum ISRayFacePerimeter
+ public enum RayFacePerimeterIntersectionStatus
{
NoIntersection, Point, Error
}
- public struct IRLineLine
+ public struct LineLineIntersectionResult
{
- public double Distance
- {
- get;
- set;
- }
-
- public double TA
- {
- get;
- set;
- }
-
- public double TB
- {
- get;
- set;
- }
-
- public Point3d PointA
- {
- get;
- set;
- }
-
- public Point3d PointB
- {
- get;
- set;
- }
+ public double Distance { get; set; }
+
+ public double ParamA { get; set; }
+
+ public double ParamB { get; set; }
+
+ public Point3d PointA { get; set; }
+
+ public Point3d PointB { get; set; }
}
}
}
\ No newline at end of file
diff --git a/src/Geometry/SpatialStructures/PointCloud.cs b/src/Geometry/SpatialStructures/PointCloud.cs
index d6c20fb..c6116e8 100644
--- a/src/Geometry/SpatialStructures/PointCloud.cs
+++ b/src/Geometry/SpatialStructures/PointCloud.cs
@@ -9,49 +9,68 @@ namespace Paramdigma.Core.SpatialSearch
///
public class PointCloud : IList
{
- private List points
- {
- get;
- }
+ ///
+ /// Constructs a new point-cloud from a list of point cloud members..
+ ///
+ /// List of point-cloud members.
+ public PointCloud(List points) => this.Points = points;
+
+
+ private List Points { get; }
+
+
+ ///
+ public IEnumerator GetEnumerator() => this.Points.GetEnumerator();
+
+
+ IEnumerator IEnumerable.GetEnumerator() => (( IEnumerable ) this.Points).GetEnumerator();
- public IEnumerator GetEnumerator() => this.points.GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.points).GetEnumerator();
+ ///
+ public void Add(PointCloudMember item) => this.Points.Add(item);
- public void Add(PointCloudMember item) => this.points.Add(item);
///
- public void Clear() => this.points.Clear();
+ public void Clear() => this.Points.Clear();
+
///
- public bool Contains(PointCloudMember item) => this.points.Contains(item);
+ public bool Contains(PointCloudMember item) => this.Points.Contains(item);
+
///
- public void CopyTo(PointCloudMember[] array, int arrayIndex) => this.points.CopyTo(array, arrayIndex);
+ public void CopyTo(PointCloudMember[] array, int arrayIndex) =>
+ this.Points.CopyTo(array, arrayIndex);
+
///
- public bool Remove(PointCloudMember item) => this.points.Remove(item);
+ public bool Remove(PointCloudMember item) => this.Points.Remove(item);
+
///
- public int Count => this.points.Count;
+ public int Count => this.Points.Count;
///
- public bool IsReadOnly => ((ICollection)this.points).IsReadOnly;
+ public bool IsReadOnly => (( ICollection ) this.Points).IsReadOnly;
+
///
- public int IndexOf(PointCloudMember item) => this.points.IndexOf(item);
+ public int IndexOf(PointCloudMember item) => this.Points.IndexOf(item);
+
///
- public void Insert(int index, PointCloudMember item) => this.points.Insert(index, item);
+ public void Insert(int index, PointCloudMember item) => this.Points.Insert(index, item);
+
///
- public void RemoveAt(int index) => this.points.RemoveAt(index);
+ public void RemoveAt(int index) => this.Points.RemoveAt(index);
+
///
public PointCloudMember this[int index]
{
- get => this.points[index];
- set => this.points[index] = value;
+ get => this.Points[index];
+ set => this.Points[index] = value;
}
}
}
\ No newline at end of file
diff --git a/src/Geometry/SpatialStructures/PointCloudMember.cs b/src/Geometry/SpatialStructures/PointCloudMember.cs
index f6b19b6..dcc19f6 100644
--- a/src/Geometry/SpatialStructures/PointCloudMember.cs
+++ b/src/Geometry/SpatialStructures/PointCloudMember.cs
@@ -12,10 +12,6 @@ public class PointCloudMember : BasePoint
/// Gets or sets the color at this point.
///
/// The current color if set, defaults to white.
- public Color Color
- {
- get;
- set;
- }
+ public Color Color { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Geometry/SpatialStructures/Quadtree.cs b/src/Geometry/SpatialStructures/Quadtree.cs
index bb9a403..1e9333d 100644
--- a/src/Geometry/SpatialStructures/Quadtree.cs
+++ b/src/Geometry/SpatialStructures/Quadtree.cs
@@ -25,6 +25,7 @@ public class QuadTree
private QuadTree southEast;
private QuadTree southWest;
+
///
/// Initializes a new instance of the class.
///
@@ -37,6 +38,7 @@ public QuadTree(BoundingBox2d boundary, double threshold)
this.threshold = threshold;
}
+
///
/// Insert a point in the QuadTree.
///
@@ -47,8 +49,8 @@ public bool Insert(Point2d point)
if (!this.Boundary.ContainsPoint(point))
return false;
-
- if (this.Boundary.XDomain.Length < this.threshold || this.Boundary.YDomain.Length < this.threshold)
+ if (this.Boundary.XDomain.Length < this.threshold
+ || this.Boundary.YDomain.Length < this.threshold)
{
this.Points.Add(point);
return true;
@@ -65,6 +67,7 @@ public bool Insert(Point2d point)
return false;
}
+
///
/// Query the QuadTree for all points contained in this range.
///
@@ -77,14 +80,16 @@ public List QueryRange(BoundingBox2d range)
if (!this.Boundary.IntersectsBox(range))
return pointsInRange;
- this.Points.ForEach(pt =>
- {
- if (range.ContainsPoint(pt))
- pointsInRange.Add(pt);
- });
+ this.Points.ForEach(
+ pt =>
+ {
+ if (range.ContainsPoint(pt))
+ pointsInRange.Add(pt);
+ });
// If we reached threshold return
- if (this.Boundary.XDomain.Length < this.threshold || this.Boundary.YDomain.Length < this.threshold)
+ if (this.Boundary.XDomain.Length < this.threshold
+ || this.Boundary.YDomain.Length < this.threshold)
return pointsInRange;
if (this.southWest != null)
@@ -98,6 +103,7 @@ public List QueryRange(BoundingBox2d range)
return pointsInRange;
}
+
private void Subdivide()
{
this.southWest = new QuadTree(
diff --git a/src/IO/CSVReader.cs b/src/IO/CSVReader.cs
index 74ef66d..a1e4ef8 100644
--- a/src/IO/CSVReader.cs
+++ b/src/IO/CSVReader.cs
@@ -5,5 +5,5 @@ namespace Paramdigma.Core.IO
///
/// CSV File reader.
///
- public static class CSVReader { }
+ public static class CsvReader { }
}
\ No newline at end of file
diff --git a/src/IO/CSVWritter.cs b/src/IO/CSVWritter.cs
index b541769..388ce40 100644
--- a/src/IO/CSVWritter.cs
+++ b/src/IO/CSVWritter.cs
@@ -5,5 +5,5 @@ namespace Paramdigma.Core.IO
///
/// CSV File Writter.
///
- public static class CSVWritter { }
+ public static class CsvWritter { }
}
\ No newline at end of file
diff --git a/src/IO/OBJMeshData.cs b/src/IO/OBJMeshData.cs
index 3eddd83..3a76584 100644
--- a/src/IO/OBJMeshData.cs
+++ b/src/IO/OBJMeshData.cs
@@ -7,34 +7,33 @@ namespace Paramdigma.Core.IO
{
public struct OBJMeshData
{
- public List Vertices
+ public OBJMeshData(
+ List vertices,
+ List> faces,
+ List> edges,
+ List> textureCoords,
+ List> faceTextureCoords,
+ List normals)
{
- get;
+ this.Vertices = vertices;
+ this.Faces = faces;
+ this.Edges = edges;
+ this.TextureCoords = textureCoords;
+ this.FaceTextureCoords = faceTextureCoords;
+ this.Normals = normals;
}
- public List> Faces
- {
- get;
- }
- public List> Edges
- {
- get;
- }
+ public List Vertices { get; }
- public List> TextureCoords
- {
- get;
- }
+ public List> Faces { get; }
- public List> FaceTextureCoords
- {
- get;
- }
+ public List> Edges { get; }
- public List Normals
- {
- get;
- }
+ public List> TextureCoords { get; }
+
+ public List> FaceTextureCoords { get; }
+
+ public List Normals { get; }
}
}
\ No newline at end of file
diff --git a/src/IO/OBJReader.cs b/src/IO/OBJReader.cs
index 4cecc4d..0ae6858 100644
--- a/src/IO/OBJReader.cs
+++ b/src/IO/OBJReader.cs
@@ -5,5 +5,5 @@ namespace Paramdigma.Core.IO
///
/// OBJ File Reader.
///
- public static class OBJReader { }
+ public static class ObjReader { }
}
\ No newline at end of file
diff --git a/src/IO/OBJWritter.cs b/src/IO/OBJWritter.cs
index b11d6f6..40b6123 100644
--- a/src/IO/OBJWritter.cs
+++ b/src/IO/OBJWritter.cs
@@ -5,5 +5,5 @@ namespace Paramdigma.Core.IO
///
/// OBJ File Writter.
///
- public class OBJWritter { }
+ public class ObjWritter { }
}
\ No newline at end of file
diff --git a/src/IO/OFFMeshData.cs b/src/IO/OFFMeshData.cs
index ad8427f..9340bd3 100644
--- a/src/IO/OFFMeshData.cs
+++ b/src/IO/OFFMeshData.cs
@@ -8,24 +8,16 @@ namespace Paramdigma.Core.IO
///
/// Class containing the resulting mesh data extracted from an .OFF file.
///
- public class OFFMeshData
+ public class OffMeshData
{
///
/// Gets or sets the mesh vertices.
///
- public List Vertices
- {
- get;
- set;
- }
+ public List Vertices { get; set; }
///
/// Gets or sets the mesh face indices.
///
- public List> Faces
- {
- get;
- set;
- }
+ public List> Faces { get; set; }
}
}
\ No newline at end of file
diff --git a/src/IO/OFFReader.cs b/src/IO/OFFReader.cs
index a86fec8..d69dce4 100644
--- a/src/IO/OFFReader.cs
+++ b/src/IO/OFFReader.cs
@@ -7,28 +7,28 @@
namespace Paramdigma.Core.IO
{
/// OFF Reader class.
- public static class OFFReader
+ public static class OffReader
{
- public static OFFResult ReadMeshFromFile(string filePath, out OFFMeshData data)
+ public static OffResult ReadMeshFromFile(string filePath, out OffMeshData data)
{
var lines = File.ReadAllLines(filePath);
- data = default;
+ data = new OffMeshData();
// Check if first line states OFF format
if (lines[0] != "OFF")
- return OFFResult.IncorrectFormat;
+ return OffResult.IncorrectFormat;
// Get second line and extract number of vertices and faces
var initialData = lines[1].Split(' ');
if (!int.TryParse(initialData[0], out var nVertex))
- return OFFResult.IncorrectFormat;
+ return OffResult.IncorrectFormat;
if (!int.TryParse(initialData[1], out var nFaces))
- return OFFResult.IncorrectFormat;
+ return OffResult.IncorrectFormat;
// Check if length of lines correct
if (nVertex + nFaces + 2 != lines.Length)
- return OFFResult.IncorrectFormat;
+ return OffResult.IncorrectFormat;
// Iterate through all the lines containing the mesh data
const int start = 2;
@@ -36,6 +36,7 @@ public static OFFResult ReadMeshFromFile(string filePath, out OFFMeshData data)
var faces = new List>();
for (var i = start; i < lines.Length; i++)
+ {
if (i < nVertex + start)
{
// Extract vertices
@@ -45,7 +46,7 @@ public static OFFResult ReadMeshFromFile(string filePath, out OFFMeshData data)
foreach (var ptStr in lines[i].Split(' '))
{
if (!double.TryParse(ptStr, out var ptCoord))
- return OFFResult.IncorrectVertex;
+ return OffResult.IncorrectVertex;
coords.Add(ptCoord);
}
@@ -60,25 +61,26 @@ public static OFFResult ReadMeshFromFile(string filePath, out OFFMeshData data)
var faceStrings = lines[i].Split(' ');
// Get first int that represents vertex count of face
- if (!int.TryParse(faceStrings[0], out var vertexCount))
- return OFFResult.IncorrectFace;
+ if (!int.TryParse(faceStrings[0], out var _))
+ return OffResult.IncorrectFace;
for (var f = 1; f < faceStrings.Length; f++)
{
if (!int.TryParse(faceStrings[f], out var vertIndex))
- return OFFResult.IncorrectFace;
+ return OffResult.IncorrectFace;
vertexIndexes.Add(vertIndex);
}
faces.Add(vertexIndexes);
}
+ }
// Set data output
data.Vertices = vertices;
data.Faces = faces;
- return OFFResult.OK;
+ return OffResult.Ok;
}
}
}
\ No newline at end of file
diff --git a/src/IO/OFFResult.cs b/src/IO/OFFResult.cs
index 061cac6..2b90ba3 100644
--- a/src/IO/OFFResult.cs
+++ b/src/IO/OFFResult.cs
@@ -5,10 +5,14 @@ namespace Paramdigma.Core.IO
///
/// Enum containing the result of the OFF conversion.
///
- public enum OFFResult
+ public enum OffResult
{
- OK, IncorrectFormat, IncorrectVertex,
- IncorrectFace, NonMatchingVerticesSize, NonMatchingFacesSize,
+ Ok,
+ IncorrectFormat,
+ IncorrectVertex,
+ IncorrectFace,
+ NonMatchingVerticesSize,
+ NonMatchingFacesSize,
FileNotFound
}
}
\ No newline at end of file
diff --git a/src/IO/OFFWritter.cs b/src/IO/OffWriter.cs
similarity index 87%
rename from src/IO/OFFWritter.cs
rename to src/IO/OffWriter.cs
index d2049a7..82cb709 100644
--- a/src/IO/OFFWritter.cs
+++ b/src/IO/OffWriter.cs
@@ -5,8 +5,8 @@
namespace Paramdigma.Core.IO
{
- /// Writter class.
- public static class OFFWritter
+ /// OFF format writer class.
+ public static class OffWriter
{
///
/// Write a Half-Edge mesh to a .OFF file.
@@ -14,7 +14,7 @@ public static class OFFWritter
/// Half-edge mesh to export.
/// Path to save the file to.
///
- public static OFFResult WriteMeshToFile(Mesh mesh, string filePath)
+ public static OffResult WriteMeshToFile(Mesh mesh, string filePath)
{
var offLines = new string[mesh.Vertices.Count + mesh.Faces.Count + 2];
@@ -32,6 +32,7 @@ public static OFFResult WriteMeshToFile(Mesh mesh, string filePath)
}
foreach (var face in mesh.Faces)
+ {
if (!face.IsBoundaryLoop())
{
var vertices = face.AdjacentVertices();
@@ -43,9 +44,10 @@ public static OFFResult WriteMeshToFile(Mesh mesh, string filePath)
offLines[count] = faceString;
count++;
}
+ }
File.WriteAllLines(filePath, offLines);
- return OFFResult.OK;
+ return OffResult.Ok;
}
}
}
\ No newline at end of file
diff --git a/src/LinearAlgebra/Complex.cs b/src/LinearAlgebra/Complex.cs
index 2f08cce..aa2a030 100644
--- a/src/LinearAlgebra/Complex.cs
+++ b/src/LinearAlgebra/Complex.cs
@@ -18,43 +18,40 @@ public Complex(double real, double imaginary)
this.Imaginary = imaginary;
}
+
///
/// Gets or sets the Real component of the complex number.
///
- public double Real
- {
- get;
- set;
- }
+ public double Real { get; set; }
///
/// Gets or sets the Imaginary component of the complex number.
///
- public double Imaginary
- {
- get;
- set;
- }
+ public double Imaginary { get; set; }
// Methods
+
///
/// Computes the phase angle of this complex number.
///
///
public double Arg() => Math.Atan2(this.Imaginary, this.Real);
+
///
/// Computes the length of the complex number.
///
///
public double Norm() => Math.Sqrt(this.Norm2());
+
///
/// Computes the squared length of the complex number.
///
///
- public double Norm2() => (this.Real * this.Real) + (this.Imaginary * this.Imaginary);
+ public double Norm2() => this.Real * this.Real + this.Imaginary * this.Imaginary;
+
///
/// Conjugates complex number (negates the imaginary component).
@@ -62,14 +59,17 @@ public double Imaginary
///
public Complex Conjugate() => new Complex(this.Real, -this.Imaginary);
+
///
/// Computes the inverse of the complex number ((a + bi)^-1).
///
///
public Complex Inverse() => this.Conjugate().OverReal(this.Norm2());
+
///
- /// Computes the polar form ae^(iθ), where a is the norm and θ is the phase angle of this complex number.
+ /// Computes the polar form ae^(iθ), where a is the norm and θ is the phase angle of this complex
+ /// number.
///
///
public Complex Polar()
@@ -80,6 +80,7 @@ public Complex Polar()
return new Complex(Math.Cos(theta) * a, Math.Sin(theta) * a);
}
+
///
/// Exponentiates this complex number.
///
@@ -91,15 +92,23 @@ public Complex Exp()
return new Complex(Math.Cos(theta) * a, Math.Sin(theta) * a);
}
+
// Private methods for operators
- private Complex Plus(Complex v) => new Complex(this.Real + v.Real, this.Imaginary + v.Imaginary);
+ private Complex Plus(Complex v) => new Complex(
+ this.Real + v.Real,
+ this.Imaginary + v.Imaginary);
+
+
+ private Complex Minus(Complex v) => new Complex(
+ this.Real - v.Real,
+ this.Imaginary - v.Imaginary);
- private Complex Minus(Complex v) => new Complex(this.Real - v.Real, this.Imaginary - v.Imaginary);
private Complex TimesReal(double s) => new Complex(this.Real * s, this.Imaginary * s);
private Complex OverReal(double s) => this.TimesReal(1 / s);
+
private Complex TimesComplex(Complex v)
{
var a = this.Real;
@@ -107,16 +116,18 @@ private Complex TimesComplex(Complex v)
var c = v.Real;
var d = v.Imaginary;
- var reNew = (a * c) - (b * d);
- var imNew = (a * d) - (b * c);
+ var reNew = a * c - b * d;
+ var imNew = a * d - b * c;
return new Complex(reNew, imNew);
}
+
private Complex OverComplex(Complex v) => this.TimesComplex(v.Inverse());
// Operators
+
///
/// Adds to complex numbers.
///
@@ -124,6 +135,7 @@ private Complex TimesComplex(Complex v)
/// Second complex number.
public static Complex operator +(Complex v, Complex w) => v.Plus(w);
+
///
/// Substracts one complex number from another.
///
@@ -131,6 +143,7 @@ private Complex TimesComplex(Complex v)
/// Second complex number.
public static Complex operator -(Complex v, Complex w) => v.Minus(w);
+
///
/// Multiplies a complex number with a number.
///
@@ -138,6 +151,7 @@ private Complex TimesComplex(Complex v)
/// Multiplier.
public static Complex operator *(Complex v, double s) => v.TimesReal(s);
+
///
/// Multiplies a complex number with a number.
///
@@ -145,6 +159,7 @@ private Complex TimesComplex(Complex v)
/// Multiplicand.
public static Complex operator *(double s, Complex v) => v.TimesReal(s);
+
///
/// Multiplies to complex numbers.
///
@@ -152,6 +167,7 @@ private Complex TimesComplex(Complex v)
/// Multiplier.
public static Complex operator *(Complex v, Complex w) => v.TimesComplex(w);
+
///
/// Divides a complex number by a number.
///
@@ -159,6 +175,7 @@ private Complex TimesComplex(Complex v)
/// Dividend.
public static Complex operator /(Complex v, double s) => v.OverReal(s);
+
///
/// Divides two complex numbers.
///
diff --git a/src/LinearAlgebra/LeastSquaresLinearFit.cs b/src/LinearAlgebra/LeastSquaresLinearFit.cs
index b43375d..3e15b1e 100644
--- a/src/LinearAlgebra/LeastSquaresLinearFit.cs
+++ b/src/LinearAlgebra/LeastSquaresLinearFit.cs
@@ -13,6 +13,7 @@ public static class LeastSquaresLinearFit
// Return the total error.
// Found at: http://csharphelper.com/blog/2014/10/find-a-linear-least-squares-fit-for-a-set-of-points-in-c/
+
///
/// Find the least squares best fitting line to the given points.
///
@@ -21,7 +22,9 @@ public static class LeastSquaresLinearFit
/// Slope.
///
public static double FindLinearLeastSquaresFit(
- List points, out double m, out double b)
+ List points,
+ out double m,
+ out double b)
{
// Perform the calculation.
// Find the values S1, Sx, Sy, Sxx, and Sxy.
@@ -37,19 +40,20 @@ public static double FindLinearLeastSquaresFit(
}
// Solve for m and b.
- m = ((sxy * s1) - (sx * sy)) / ((sxx * s1) - (sx * sx));
- b = ((sxy * sx) - (sy * sxx)) / ((sx * sx) - (s1 * sxx));
+ m = (sxy * s1 - sx * sy) / (sxx * s1 - sx * sx);
+ b = (sxy * sx - sy * sxx) / (sx * sx - s1 * sxx);
return Math.Sqrt(ErrorSquared(points, m, b));
}
+
// Return the error squared.
private static double ErrorSquared(List points, double m, double b)
{
double total = 0;
foreach (var pt in points)
{
- var dy = pt.Y - ((m * pt.X) + b);
+ var dy = pt.Y - (m * pt.X + b);
total += dy * dy;
}
diff --git a/src/LinearAlgebra/Triplet.cs b/src/LinearAlgebra/Triplet.cs
index 7bb90d1..67aa53a 100644
--- a/src/LinearAlgebra/Triplet.cs
+++ b/src/LinearAlgebra/Triplet.cs
@@ -18,24 +18,17 @@ public Triplet(int m, int n)
}
// Public fields
+
///
/// Gets values held by this triplet.
///
///
- public List Values
- {
- get;
- }
+ public List Values { get; }
- public int M
- {
- get;
- }
+ public int M { get; }
+
+ public int N { get; }
- public int N
- {
- get;
- }
// Methods
public void AddEntry(double value, int m, int n)
@@ -48,22 +41,10 @@ public void AddEntry(double value, int m, int n)
public struct TripletData
{
- public int Row
- {
- get;
- set;
- }
+ public int Row { get; set; }
- public int Column
- {
- get;
- set;
- }
+ public int Column { get; set; }
- public double Value
- {
- get;
- set;
- }
+ public double Value { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Optimization/GradientDescent.cs b/src/Optimization/GradientDescent.cs
index aef3742..4bdd106 100644
--- a/src/Optimization/GradientDescent.cs
+++ b/src/Optimization/GradientDescent.cs
@@ -29,6 +29,7 @@ public class GradientDescent
///
public GradientDescentResult Result;
+
///
/// Initializes a new instance of the class with given options.
///
@@ -39,6 +40,7 @@ public GradientDescent(GradientDescentOptions options)
this.Options = options;
}
+
///
/// Run gradient descent algorithm.
///
@@ -66,12 +68,14 @@ public void Minimize(FitnessFunction function, List inputValues)
this.Result.Error = function(this.Result.Values);
this.Result.GradientLength = gLength;
iter++; // Increase iteration count
- } while (gLength > this.Options.Limit && iter < this.Options.MaxIterations && this.Result.Error > this.Options.ErrorThreshold);
+ } while (gLength > this.Options.Limit && iter < this.Options.MaxIterations
+ && this.Result.Error
+ > this.Options.ErrorThreshold);
- var customError = this.Result.Error > 1E5 ? $"{this.Result.Error:0.###e-000}" : $"{this.Result.Error:0.00000}";
Console.ResetColor();
}
+
///
/// Computes the gradient vector of a given function at some specified input values.
///
@@ -87,7 +91,11 @@ private List ComputeGradient(FitnessFunction func, List inputVal
for (var i = 0; i < inputValues.Count; i++)
{
- var dV = this.ComputePartialDerivative(i, func, inputValues, this.Options.DerivativeStep);
+ var dV = this.ComputePartialDerivative(
+ i,
+ func,
+ inputValues,
+ this.Options.DerivativeStep);
gradient.Add(dV * this.Options.LearningRate);
derivativeSquareSum += dV * dV;
}
@@ -102,6 +110,7 @@ private List ComputeGradient(FitnessFunction func, List inputVal
return gradient;
}
+
///
/// Computes the partial derivative at a given input value of a given function.
///
@@ -126,7 +135,7 @@ private double ComputePartialDerivative(
inputValues[inputIndex] += step; // Reset value to original
// Compute partial derivative using 2-point method
- partialDerivative = ((error1 - error2) / 2) * step;
+ partialDerivative = (error1 - error2) / 2 * step;
return partialDerivative;
}
diff --git a/src/Optimization/GradientDescentOptions.cs b/src/Optimization/GradientDescentOptions.cs
index 0cc29cc..afe466e 100644
--- a/src/Optimization/GradientDescentOptions.cs
+++ b/src/Optimization/GradientDescentOptions.cs
@@ -30,8 +30,10 @@ public struct GradientDescentOptions
///
public double ErrorThreshold;
+
///
- /// Initializes a new instance of the struct given an existing one.
+ /// Initializes a new instance of the struct given an
+ /// existing one.
///
/// Options to duplicate.
public GradientDescentOptions(GradientDescentOptions options)
@@ -43,17 +45,25 @@ public GradientDescentOptions(GradientDescentOptions options)
this.ErrorThreshold = options.ErrorThreshold;
}
+
// TODO: Fill in this fields!
+
///
- /// Initializes a new instance of the struct given all it's values individually.
+ /// Initializes a new instance of the struct given all it's
+ /// values individually.
///
///
///
///
///
///
- public GradientDescentOptions(double threshold, int maxIterations, double derivativeStep, double learningRate, double errorThreshold)
+ public GradientDescentOptions(
+ double threshold,
+ int maxIterations,
+ double derivativeStep,
+ double learningRate,
+ double errorThreshold)
{
this.Limit = threshold;
this.MaxIterations = maxIterations;
@@ -62,15 +72,18 @@ public GradientDescentOptions(double threshold, int maxIterations, double deriva
this.ErrorThreshold = errorThreshold;
}
+
///
/// Gets a GradientDescentOptions instance with the default values.
///
- public static GradientDescentOptions Default => new GradientDescentOptions(0.001, 10000, 0.01, 20, .01);
+ public static GradientDescentOptions Default =>
+ new GradientDescentOptions(0.001, 10000, 0.01, 20, .01);
///
/// Gets a GradientDescentOptions instance with small values.
///
///
- public static GradientDescentOptions DefaultSmall => new GradientDescentOptions(0.0001, 10000, 0.02, 40, .001);
+ public static GradientDescentOptions DefaultSmall =>
+ new GradientDescentOptions(0.0001, 10000, 0.02, 40, .001);
}
}
\ No newline at end of file
diff --git a/src/Optimization/KMeansCluster.cs b/src/Optimization/KMeansCluster.cs
index c105007..ca7915a 100644
--- a/src/Optimization/KMeansCluster.cs
+++ b/src/Optimization/KMeansCluster.cs
@@ -31,38 +31,49 @@ public VectorNd this[int index]
///
public bool IsReadOnly => this.list.IsReadOnly;
+
///
/// Add a new vector to the cluster.
///
/// Vector to add.
public void Add(VectorNd item) => this.list.Add(item);
+
///
public void Clear() => this.list.Clear();
+
///
public bool Contains(VectorNd item) => this.list.Contains(item);
+
///
public void CopyTo(VectorNd[] array, int arrayIndex) => this.list.CopyTo(array, arrayIndex);
+
///
public IEnumerator GetEnumerator() => this.list.GetEnumerator();
+
///
public int IndexOf(VectorNd item) => this.list.IndexOf(item);
+
///
public void Insert(int index, VectorNd item) => this.list.Insert(index, item);
+
///
public bool Remove(VectorNd item) => this.list.Remove(item);
+
///
public void RemoveAt(int index) => this.list.RemoveAt(index);
+
IEnumerator IEnumerable.GetEnumerator() => this.list.GetEnumerator();
+
///
/// Computes the average of this cluster.
///
@@ -82,6 +93,7 @@ public VectorNd Average()
return result;
}
+
///
public override string ToString() => "Paramdigma.Core.Cluster[" + this.Count + "]";
}
diff --git a/src/Optimization/KMeansClustering.cs b/src/Optimization/KMeansClustering.cs
index f133cb2..1958120 100644
--- a/src/Optimization/KMeansClustering.cs
+++ b/src/Optimization/KMeansClustering.cs
@@ -14,6 +14,7 @@ public class KMeansClustering
private readonly int maxIterations;
private int currentIterations;
+
///
/// Initializes a new instance of the class.
///
@@ -27,14 +28,12 @@ public KMeansClustering(int maxIterations, int clusterCount, List data
this.InitializeClusters(data);
}
+
///
/// Gets or sets the list of clusters.
///
- public List Clusters
- {
- get;
- set;
- }
+ public List Clusters { get; set; }
+
private void InitializeClusters(List data)
{
@@ -42,18 +41,22 @@ private void InitializeClusters(List data)
for (var i = 0; i < this.clusterCount; i++)
this.Clusters.Add(new KMeansCluster());
- data.ForEach(vector => this.Clusters[new Random().Next() % this.clusterCount].Add(vector));
+ data.ForEach(
+ vector => this.Clusters[new Random().Next() % this.clusterCount].Add(vector));
}
+
///
/// Run the algorithm until it reaches the maximum amount of iterations.
///
public void Run() => this.Run(this.maxIterations, false);
+
///
/// Run the k-means clustering algorithm for a specified amount of iterations.
///
/// Iterations to run.
+ /// True to allow the optimization to leave clusters empty.
public void Run(int iterations, bool allowEmptyClusters)
{
var rnd = new Random();
@@ -73,39 +76,46 @@ public void Run(int iterations, bool allowEmptyClusters)
newClusters.Add(new KMeansCluster());
// Find the closest average for each vector in each cluster
- this.Clusters.ForEach(cluster =>
- {
- var ind = this.Clusters.IndexOf(cluster);
- for (var i = 0; i < cluster.Count; i++)
+ this.Clusters.ForEach(
+ cluster =>
{
- var vector = cluster[i];
- var simIndex = this.FindIndexOfSimilar(averages, vector);
- newClusters[simIndex].Add(vector);
- if (simIndex != ind)
- hasChanged = true;
- }
- });
+ var ind = this.Clusters.IndexOf(cluster);
+ for (var i = 0; i < cluster.Count; i++)
+ {
+ var vector = cluster[i];
+ var simIndex = this.FindIndexOfSimilar(averages, vector);
+ newClusters[simIndex].Add(vector);
+ if (simIndex != ind)
+ hasChanged = true;
+ }
+ });
// Check for empty clusters
if (!allowEmptyClusters)
- newClusters.ForEach(cluster =>
- {
- if (cluster.Count == 0)
+ {
+ newClusters.ForEach(
+ cluster =>
{
- Console.WriteLine("Cluster has no mass");
- var biggest = newClusters.OrderByDescending(x => x.Count)
- .First();
-
- var randomVector = biggest[rnd.Next(biggest.Count)];
-
- biggest.Remove(randomVector);
- cluster.Add(randomVector);
- }
- });
+ if (cluster.Count == 0)
+ {
+ Console.WriteLine("Cluster has no mass");
+ var biggest = newClusters.OrderByDescending(x => x.Count)
+ .First();
+
+ var randomVector = biggest[rnd.Next(biggest.Count)];
+
+ biggest.Remove(randomVector);
+ cluster.Add(randomVector);
+ }
+ });
+ }
// Update clusters and increase iteration
this.Clusters = newClusters;
- var iterArgs = new IterationCompletedEventArgs {iteration = iteration, Clusters = newClusters};
+ var iterArgs = new IterationCompletedEventArgs
+ {
+ Iteration = iteration, Clusters = newClusters
+ };
this.OnIterationCompleted(iterArgs);
iteration++;
this.currentIterations++;
@@ -114,6 +124,7 @@ public void Run(int iterations, bool allowEmptyClusters)
&& this.currentIterations < this.maxIterations);
}
+
///
/// Find the index of the most similar vector to a given one.
///
@@ -139,33 +150,35 @@ public int FindIndexOfSimilar(List pool, VectorNd vector)
return minIndex;
}
+
///
/// Raised when an iteration is completed.
///
public event EventHandler IterationCompleted;
+
///
/// Method to call when an iteration is completed.
///
/// Data for the current iteration.
- protected virtual void OnIterationCompleted(IterationCompletedEventArgs iterArgs) => this.IterationCompleted?.Invoke(this, iterArgs);
+ protected virtual void OnIterationCompleted(IterationCompletedEventArgs iterArgs) =>
+ this.IterationCompleted?.Invoke(this, iterArgs);
+
///
/// Data for the current iteration event.
///
public class IterationCompletedEventArgs : EventArgs
{
- public int iteration
- {
- get;
- set;
- }
-
- public List Clusters
- {
- get;
- set;
- }
+ ///
+ /// Iteration number.
+ ///
+ public int Iteration { get; set; }
+
+ ///
+ /// Clusters for current iteration.
+ ///
+ public List Clusters { get; set; }
}
}
}
\ No newline at end of file
diff --git a/src/Paramdigma.Core.csproj b/src/Paramdigma.Core.csproj
index 534717c..05e524e 100644
--- a/src/Paramdigma.Core.csproj
+++ b/src/Paramdigma.Core.csproj
@@ -4,16 +4,17 @@
Computational Geometry library for .NET
- netstandard2.0
+ netstandard2.1
Paramdigma Core
https://paramdigma.com/Core/
https://github.com/Paramdigma/Core/blob/master/LICENSE
git
+ 8
Paramdigma.Core
- 0.0.9
+ 0.1.0
Alan Rynne
Paramdigma
Computational Geometry library for .Net
@@ -24,10 +25,6 @@
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
diff --git a/src/Utility/Convert.cs b/src/Utility/Convert.cs
index dd81edf..fa0bb42 100644
--- a/src/Utility/Convert.cs
+++ b/src/Utility/Convert.cs
@@ -20,10 +20,10 @@ public static double[] Point3dToBarycentric(Point3d p, Point3d a, Point3d b, Poi
{
Vector3d v0 = b - a, v1 = c - a, v2 = p - a;
- var den = (v0.X * v1.Y) - (v1.X * v0.Y);
+ var den = v0.X * v1.Y - v1.X * v0.Y;
- var v = ((v2.X * v1.Y) - (v1.X * v2.Y)) / den;
- var w = ((v0.X * v2.Y) - (v2.X * v0.Y)) / den;
+ var v = (v2.X * v1.Y - v1.X * v2.Y) / den;
+ var w = (v0.X * v2.Y - v2.X * v0.Y) / den;
var u = 1.0 - v - w;
double[] result = {u, v, w};
diff --git a/src/Utility/Settings.cs b/src/Utility/Settings.cs
index de393a7..fec2ef8 100644
--- a/src/Utility/Settings.cs
+++ b/src/Utility/Settings.cs
@@ -1,3 +1,4 @@
+using System;
using System.IO;
using System.Reflection;
using Newtonsoft.Json;
@@ -14,11 +15,7 @@ public static class Settings
///
/// Gets the minimum value allowed when using this library.
///
- public static double Tolerance
- {
- get;
- private set;
- } = 0.0000001;
+ public static double Tolerance { get; private set; } = 0.0000001;
///
/// Gets how many decimals are allowed when using the library.
@@ -32,46 +29,54 @@ public static int MaxDecimals
}
}
+
///
/// Gets the default tessellation level when converting nurbs to meshes.
///
/// Integer representing the default tessellation level.
public static int GetDefaultTesselationLevel() => tesselationLevel;
+
///
/// Sets the default tessellation level when converting nurbs to meshes.
///
private static void SetDefaultTesselationLevel(int value) => tesselationLevel = value;
+
///
/// Modifies the tolerance and computes the maxDecimals value accordingly.
///
/// Desired tolerance.
public static void SetTolerance(double tolerance) => Tolerance = tolerance;
+
///
/// Reset the Settings to it's default values.
///
public static void Reset()
{
var assembly = typeof(Settings).GetTypeInfo().Assembly;
- using (var stream = assembly.GetManifestResourceStream("Paramdigma.Core.Data.Settings.json"))
- using (var reader = new StreamReader(stream))
+ using (var stream =
+ assembly.GetManifestResourceStream("Paramdigma.Core.Data.Settings.json"))
{
- var result = reader.ReadToEnd();
- var json = JsonConvert.DeserializeObject(result);
- SetTolerance(json.Tolerance);
- SetDefaultTesselationLevel(json.DefaultTesselation);
+ using (var reader = new StreamReader(
+ stream ?? throw new InvalidOperationException("Could not get settings.")))
+ {
+ var result = reader.ReadToEnd();
+ var json = JsonConvert.DeserializeObject(result);
+ SetTolerance(json.Tolerance);
+ SetDefaultTesselationLevel(json.DefaultTesselation);
+ }
}
}
+ }
- ///
- /// This struct holds the settings from the embedded json file. It is only used to reset.
- ///
- private struct EmbeddedSettings
- {
- public double Tolerance;
- public int DefaultTesselation;
- }
+ ///
+ /// This struct holds the settings from the embedded json file. It is only used to reset.
+ ///
+ internal struct EmbeddedSettings
+ {
+ public double Tolerance;
+ public int DefaultTesselation;
}
}
\ No newline at end of file
diff --git a/tests/Collections/IntervalTests.cs b/tests/Collections/IntervalTests.cs
index 2ecbaf1..358339a 100644
--- a/tests/Collections/IntervalTests.cs
+++ b/tests/Collections/IntervalTests.cs
@@ -16,6 +16,7 @@ public void Can_CheckAndModifyDirection()
Assert.True(dir != dir2);
}
+
[Fact]
public void Can_CheckContainment()
{
@@ -28,6 +29,7 @@ public void Can_CheckContainment()
Assert.True(i.Contains(n3));
}
+
[Fact]
public void Can_CropNumbers()
{
@@ -35,32 +37,31 @@ public void Can_CropNumbers()
const double n1 = 0.0;
const double n2 = 5.0;
const double n3 = 2.33;
- var n1c = i.Crop(n1);
- var n2c = i.Crop(n2);
- var n3c = i.Crop(n3);
- Assert.True(n1c == i.Start);
- Assert.True(n2c == i.End);
- Assert.True(n3c == n3);
+ var n1C = i.Crop(n1);
+ var n2C = i.Crop(n2);
+ var n3C = i.Crop(n3);
+ Assert.True(Math.Abs(n1C - i.Start) < Settings.Tolerance);
+ Assert.True(Math.Abs(n2C - i.End) < Settings.Tolerance);
+ Assert.True(Math.Abs(n3C - n3) < Settings.Tolerance);
}
+
[Fact]
public void Can_RemapNumbers()
{
var i = new Interval(1, 3);
const double n = 2.0;
var nMap = i.RemapToUnit(n);
- Assert.True(nMap == 0.5);
+ Assert.True(Math.Abs(nMap - 0.5) < Settings.Tolerance);
var nRemap = i.RemapFromUnit(nMap);
- Assert.True(n == nRemap);
+ Assert.True(Math.Abs(n - nRemap) < Settings.Tolerance);
}
+
[Fact]
public void CanCreate_Interval()
{
- var i0 = new Interval(0, 1);
- var i1 = Interval.Unit;
- var i2 = new Interval(i0);
-
+ var i0 = Interval.Unit;
Assert.Equal(1, i0.Length);
Assert.Throws(() => new Interval(double.NaN, 1));
Assert.Throws(() => new Interval(0, double.NaN));
diff --git a/tests/Curves/LevelSetsTests.cs b/tests/Curves/LevelSetsTests.cs
index 2d670e0..6275039 100644
--- a/tests/Curves/LevelSetsTests.cs
+++ b/tests/Curves/LevelSetsTests.cs
@@ -26,6 +26,7 @@ public Mesh Triangle
}
}
+
[Fact]
public void CanCompute_GradientInFace_ReturnsValidVector()
{
@@ -34,12 +35,17 @@ public void CanCompute_GradientInFace_ReturnsValidVector()
Assert.Equal(new Vector3d(0, -1, -1).Unit(), c[0].Unit());
}
+
[Fact]
public void CanCompute_LevelInFace_ReturnsValidLine()
{
- LevelSets.ComputeLevels("scalar-1", new List {0.5}, this.Triangle, out var levelSets);
+ LevelSets.ComputeLevels(
+ "scalar-1",
+ new List {0.5},
+ this.Triangle,
+ out var levelSets);
Assert.NotEmpty(levelSets);
- var v = (Vector3d)levelSets[0][0];
+ var v = ( Vector3d ) levelSets[0][0];
Assert.Equal(new Vector3d(1, 0, 0), v.Unit());
}
}
diff --git a/tests/Extensions/ListExtensionsTests.cs b/tests/Extensions/ListExtensionsTests.cs
index 291561e..32415fd 100644
--- a/tests/Extensions/ListExtensionsTests.cs
+++ b/tests/Extensions/ListExtensionsTests.cs
@@ -16,6 +16,7 @@ public void CanCreate_Repeated()
}
}
+
[Fact]
public void CanCreate_RepeatedDefault()
{
diff --git a/tests/Geometry/2D/DelaunayTests.cs b/tests/Geometry/2D/DelaunayTests.cs
index 9518644..469970b 100644
--- a/tests/Geometry/2D/DelaunayTests.cs
+++ b/tests/Geometry/2D/DelaunayTests.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using Paramdigma.Core.Geometry;
using Xunit;
@@ -7,62 +8,89 @@ namespace Paramdigma.Core.Tests.Geometry
{
public class DelaunayTests
{
- private List GeneratePoints(int amout, double maxX, double maxY, out List border)
+ [Fact]
+ public void CanComputeDelaunay()
{
+ var maxX = 100;
+ var maxY = 100;
var point0 = new DelaunayPoint(0, 0);
var point1 = new DelaunayPoint(0, maxY);
var point2 = new DelaunayPoint(maxX, maxY);
var point3 = new DelaunayPoint(maxX, 0);
- var points = new List {point0, point1, point2, point3};
+ var point4 = new DelaunayPoint(maxX / 2.0, maxY / 2.0);
+
var triangle1 = new DelaunayTriangle(point0, point1, point2);
var triangle2 = new DelaunayTriangle(point0, point2, point3);
- border = new List {triangle1, triangle2};
- var rnd = new Random();
- var points2 = new List();
- for (var i = 0; i < amout - 4; i++)
- points2.Add(RandomPoint(rnd, 0, maxX));
- return points2;
- }
+ var border = new List {triangle1, triangle2};
+ var delaunay = Delaunay.Compute(
+ new List
+ {
+ point0, point1, point2, point3, point4
+ },
+ border
+ )
+ .ToList();
+ Assert.True(delaunay.Count == 4);
- public static DelaunayPoint RandomPoint(Random RandGenerator, double MinValue, double MaxValue)
- {
- var range = MaxValue - MinValue;
- var randomPoint = new DelaunayPoint((RandGenerator.NextDouble() * range) + MinValue, (RandGenerator.NextDouble() * range) + MinValue);
- return randomPoint;
+ var voronoi = Delaunay.Voronoi(delaunay);
+ // TODO: We expect 8 because it currently outputs the lines repeated twice.
+ Assert.True(voronoi.ToList().Count == 8);
}
- [Fact]
- public void CanComputeDelaunay()
+
+ private List GeneratePoints(
+ int ammount,
+ double maxX,
+ double maxY,
+ out List border)
{
- var maxX = 100;
- var maxY = 100;
var point0 = new DelaunayPoint(0, 0);
var point1 = new DelaunayPoint(0, maxY);
var point2 = new DelaunayPoint(maxX, maxY);
var point3 = new DelaunayPoint(maxX, 0);
- var point4 = new DelaunayPoint(maxX / 2, maxY / 2);
- var points = new List {point0, point1, point2, point3};
var triangle1 = new DelaunayTriangle(point0, point1, point2);
var triangle2 = new DelaunayTriangle(point0, point2, point3);
- var border = new List {triangle1, triangle2};
+ border = new List {triangle1, triangle2};
+ var rnd = new Random();
+ var points = new List();
+ for (var i = 0; i < ammount - 4; i++)
+ points.Add(RandomPoint(rnd, 0, maxX));
+ return points;
+ }
- var delaunay = Delaunay.Compute(
- new List
- {
- point0,
- point1,
- point2,
- point3,
- point4
- },
- border
- );
- Assert.True(delaunay.Count == 4);
- var voronoi = Delaunay.Voronoi(delaunay);
- // TODO: We expect 8 because it currently outputs the lines repeated twice.
- Assert.True(voronoi.Count == 8);
+ private static DelaunayPoint RandomPoint(
+ Random randGenerator,
+ double minValue,
+ double maxValue)
+ {
+ var range = maxValue - minValue;
+ var randomPoint = new DelaunayPoint(
+ randGenerator.NextDouble() * range + minValue,
+ randGenerator.NextDouble() * range + minValue);
+ return randomPoint;
+ }
+
+
+ [Fact]
+ public void CanCompare_DelaunayEdges()
+ {
+ var edgeA = new DelaunayEdge(new DelaunayPoint(0, 0), new DelaunayPoint(1, 0));
+ var edgeB = new DelaunayEdge(new DelaunayPoint(0, 0), new DelaunayPoint(1, 0));
+ Assert.Equal(edgeA, edgeB);
+ Assert.Equal(edgeA.GetHashCode(), edgeB.GetHashCode());
+ Assert.NotNull(edgeA);
+ }
+
+
+ [Fact]
+ public void CanCreate_DelaunayPoint_FromPoint2d()
+ {
+ var pt = new Point2d(.5, .5);
+ var dpt = new DelaunayPoint(pt);
+ Assert.Equal(pt.X, dpt.X);
+ Assert.Equal(pt.Y, dpt.Y);
}
}
}
\ No newline at end of file
diff --git a/tests/Geometry/2D/Line2dTests.cs b/tests/Geometry/2D/Line2dTests.cs
index c14f927..b1a46cc 100644
--- a/tests/Geometry/2D/Line2dTests.cs
+++ b/tests/Geometry/2D/Line2dTests.cs
@@ -14,6 +14,10 @@ public void CanBe_Created()
var line = new Line2d(ptA, ptB);
var lineB = new Line2d(ptA, v);
var lineC = new Line2d(ptA, v, 3);
+
+ Assert.NotNull(line);
+ Assert.NotNull(lineB);
+ Assert.NotNull(lineC);
}
}
}
\ No newline at end of file
diff --git a/tests/Geometry/2D/Point2dTests.cs b/tests/Geometry/2D/Point2dTests.cs
index 73c2deb..e5aaebc 100644
--- a/tests/Geometry/2D/Point2dTests.cs
+++ b/tests/Geometry/2D/Point2dTests.cs
@@ -15,6 +15,7 @@ public void Can_AddVector()
Assert.True(pt2 == expected);
}
+
[Fact]
public void CanBe_Added()
{
@@ -23,20 +24,21 @@ public void CanBe_Added()
const double c = 4.11;
var ptA = new Point2d(a, b);
var ptB = new Point2d(b, c);
- var s = ptA - ptB;
var ptResult = new Point2d(a + b, b + c);
Assert.True(ptA + ptB == ptResult);
}
+
[Fact]
public void CanBe_ConvertedToVector()
{
var pt = new Point2d(0, 1);
Vector2d v = pt;
- var pt2 = (Point2d)v;
+ var pt2 = ( Point2d ) v;
Assert.True(pt == pt2);
}
+
[Fact]
public void CanBe_Divided()
{
@@ -48,6 +50,7 @@ public void CanBe_Divided()
Assert.True(ptA / m == ptResult);
}
+
[Fact]
public void CanBe_Multiplied()
{
@@ -60,6 +63,7 @@ public void CanBe_Multiplied()
Assert.True(m * ptA == ptResult);
}
+
[Fact]
public void CanBe_Negated()
{
@@ -70,6 +74,7 @@ public void CanBe_Negated()
Assert.True(-ptA == ptResult);
}
+
[Fact]
public void CanBe_Substracted()
{
@@ -82,6 +87,7 @@ public void CanBe_Substracted()
Assert.True(ptA - ptB == ptResult);
}
+
[Fact]
public void Create_FromPoint()
{
@@ -90,6 +96,7 @@ public void Create_FromPoint()
Assert.True(expected == copy);
}
+
[Fact]
public void Create_Origin()
{
@@ -101,6 +108,7 @@ public void Create_Origin()
Assert.True(empty == expected);
}
+
[Fact]
public void EqualsAndHashCode_HaveConsistentResults()
{
@@ -108,10 +116,8 @@ public void EqualsAndHashCode_HaveConsistentResults()
var pt2 = new Point2d(1, -1);
var b1 = pt == pt2;
var b2 = pt.GetHashCode() == pt2.GetHashCode();
- var other = "S";
Assert.True(b1 && b1 == b2);
Assert.False(pt != pt2);
- Assert.False(pt.Equals(other));
}
}
}
\ No newline at end of file
diff --git a/tests/Geometry/2D/Polyline2dTests.cs b/tests/Geometry/2D/Polyline2dTests.cs
index fd47c08..3874e9f 100644
--- a/tests/Geometry/2D/Polyline2dTests.cs
+++ b/tests/Geometry/2D/Polyline2dTests.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections;
using System.Collections.Generic;
using Paramdigma.Core.Geometry;
@@ -15,12 +16,26 @@ public class Polyline2dUnitSquareAndSegments : IEnumerable