diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100755
index 96caae0..0000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,52 +0,0 @@
-version: 2.1
-
-orbs:
- win: circleci/windows@5.0.0
-
-jobs:
- build:
- executor: win/default
- steps:
- - checkout
- - run:
- name: Restore packages
- command: dotnet restore GrasshopperAsyncComponent.sln
- - run:
- name: Build solution
- command: |
- $TAG = if([string]::IsNullOrEmpty($env:CIRCLE_TAG)) { "0.999.$($env:WORKFLOW_NUM)-ci" } else { $env:CIRCLE_TAG }
- dotnet build GrasshopperAsyncComponent.sln --no-restore -c Release /p:Version=$TAG /p:AssemblyVersionNumber=$TAG /p:AssemblyInformationalVersion=$TAG
- environment:
- WORKFLOW_NUM: << pipeline.number >>
- publish_nuget:
- executor: win/default
- steps:
- - checkout
- - run:
- name: Restore packages
- command: dotnet restore GrasshopperAsyncComponent.sln
- - run:
- name: Build solution
- command: |
- $TAG = if([string]::IsNullOrEmpty($env:CIRCLE_TAG)) { "0.999.$($env:WORKFLOW_NUM)-ci" } else { $env:CIRCLE_TAG }
- dotnet build GrasshopperAsyncComponent.sln --no-restore -c Release /p:Version=$TAG /p:AssemblyVersionNumber=$TAG /p:AssemblyInformationalVersion=$TAG
- environment:
- WORKFLOW_NUM: << pipeline.number >>
- - run:
- name: Push NuGet
- command: nuget push **/*.nupkg -Source https://api.nuget.org/v3/index.json -ApiKey $env:NUGET_APIKEY -SkipDuplicate
-
-workflows:
- build:
- jobs:
- - build:
- context: github-dev-bot
- publish:
- jobs:
- - publish_nuget:
- filters:
- tags:
- only: /.*/
- branches:
- ignore: /.*/
- context: nuget
diff --git a/.csharpierignore b/.csharpierignore
new file mode 100644
index 0000000..072d074
--- /dev/null
+++ b/.csharpierignore
@@ -0,0 +1,26 @@
+Directory.Build.targets
+Directory.Build.props
+
+**/bin/*
+**/obj/*
+_ReSharper.SharpCompress/
+bin/
+*.suo
+*.user
+TestArchives/Scratch/
+TestArchives/Scratch2/
+TestResults/
+*.nupkg
+packages/*/
+project.lock.json
+tests/TestArchives/Scratch
+.vs
+tools
+.vscode
+.idea/
+
+.DS_Store
+*.snupkg
+coverage.xml
+
+*.received.*
\ No newline at end of file
diff --git a/.csharpierrc.yaml b/.csharpierrc.yaml
new file mode 100644
index 0000000..b8bb425
--- /dev/null
+++ b/.csharpierrc.yaml
@@ -0,0 +1,7 @@
+printWidth: 120
+useTabs: false
+indentSize: 2
+preprocessorSymbolSets:
+ - ""
+ - "DEBUG"
+ - "DEBUG,CODE_STYLE"
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..27feb2d
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,327 @@
+root = true
+# Don't use tabs for indentation.
+[*]
+indent_style = space
+
+# Microsoft .NET properties
+csharp_using_directive_placement = outside_namespace:silent
+
+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
+
+
+# Standard properties
+insert_final_newline = true
+
+# (Please don't specify an indent_size here; that has too many unintended consequences.)
+
+# Code files
+[*.{cs,csx,vb,vbx}]
+indent_size = 2
+charset = utf-8
+
+# Xml project files
+[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
+indent_size = 2
+space_after_last_pi_attribute = false
+# Xml config files
+[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
+indent_size = 2
+space_after_last_pi_attribute = false
+
+# JSON files
+[*.json]
+indent_size = 2
+
+# Dotnet code style settings:
+[*.{cs,vb}]
+# Sort using and Import directives with System.* appearing first
+dotnet_sort_system_directives_first = true
+dotnet_separate_import_directive_groups = false
+
+# Avoid "this." and "Me." if not necessary
+dotnet_style_qualification_for_field = false:suggestion
+dotnet_style_qualification_for_property = false:suggestion
+dotnet_style_qualification_for_method = false:suggestion
+dotnet_style_qualification_for_event = false:suggestion
+
+# Use language keywords instead of framework type names for type references
+dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
+dotnet_style_predefined_type_for_member_access = true:suggestion
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:silent
+dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:silent
+dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:silent
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
+dotnet_style_readonly_field = true:suggestion
+
+# Expression-level preferences
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_auto_properties = true:warning
+dotnet_style_prefer_conditional_expression_over_assignment = true:silent
+dotnet_style_prefer_conditional_expression_over_return = true:silent
+
+
+# CSharp code style settings:
+[*.cs]
+# Prefer "var" everywhere
+csharp_style_var_elsewhere = false:none
+csharp_style_var_for_built_in_types = false:none
+csharp_style_var_when_type_is_apparent = false:none
+
+# Prefer method-like constructs to have a block body
+csharp_style_expression_bodied_methods = true:suggestion
+csharp_style_expression_bodied_constructors = false:suggestion
+csharp_style_expression_bodied_operators = true:suggestion
+
+# Prefer property-like constructs to have an expression-body
+csharp_style_expression_bodied_properties = true:suggestion
+csharp_style_expression_bodied_indexers = true:suggestion
+csharp_style_expression_bodied_accessors = true:suggestion
+
+# Suggest more modern language features when available
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+csharp_style_throw_expression = true:suggestion
+csharp_style_conditional_delegate_call = true:suggestion
+csharp_style_namespace_declarations = file_scoped
+
+# Newline settings
+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
+
+# Space preferences
+csharp_space_after_cast = false
+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
+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_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+
+# Wrapping preferences
+csharp_preserve_single_line_statements = true
+csharp_preserve_single_line_blocks = true
+
+
+
+# SYMBOL NAMING RULES
+# Copied from https://github.com/dotnet/roslyn/blob/main/.editorconfig
+# Adapted rules:
+# - Constants are ALL_UPPER
+# - Non-private fields are PascalCase
+
+# Non-private fields are PascalCase
+dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = warning
+dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields
+dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style
+
+dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected
+
+dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case
+
+# Constants are ALL_UPPER
+dotnet_naming_rule.constants_should_be_all_upper.severity = warning
+dotnet_naming_rule.constants_should_be_all_upper.symbols = constants
+dotnet_naming_rule.constants_should_be_all_upper.style = constant_style
+
+dotnet_naming_symbols.constants.applicable_kinds = field, local
+dotnet_naming_symbols.constants.required_modifiers = const
+
+dotnet_naming_style.constant_style.capitalization = all_upper
+
+# Private static fields are camelCase and start with s_
+dotnet_naming_rule.static_fields_should_be_camel_case.severity = warning
+dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields
+dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style
+
+dotnet_naming_symbols.static_fields.applicable_accessibilities = private
+dotnet_naming_symbols.static_fields.applicable_kinds = field
+dotnet_naming_symbols.static_fields.required_modifiers = static
+
+dotnet_naming_style.static_field_style.capitalization = camel_case
+dotnet_naming_style.static_field_style.required_prefix = s_
+
+
+# Instance fields are camelCase and start with _
+dotnet_naming_rule.instance_fields_should_be_camel_case.severity = warning
+dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields
+dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style
+
+dotnet_naming_symbols.instance_fields.applicable_kinds = field
+
+dotnet_naming_style.instance_field_style.capitalization = camel_case
+dotnet_naming_style.instance_field_style.required_prefix = _
+
+# Locals and parameters are camelCase
+dotnet_naming_rule.locals_should_be_camel_case.severity = warning
+dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters
+dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style
+
+dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local
+
+dotnet_naming_style.camel_case_style.capitalization = camel_case
+
+# Local functions are PascalCase
+dotnet_naming_rule.local_functions_should_be_pascal_case.severity = warning
+dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions
+dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style
+
+dotnet_naming_symbols.local_functions.applicable_kinds = local_function
+
+dotnet_naming_style.local_function_style.capitalization = pascal_case
+
+# By default, name items with PascalCase
+dotnet_naming_rule.members_should_be_pascal_case.severity = warning
+dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members
+dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style
+
+dotnet_naming_symbols.all_members.applicable_kinds = *
+
+dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+
+
+# Analyzer settings
+dotnet_analyzer_diagnostic.category-Style.severity = warning # All rules will use this severity unless overriden
+dotnet_diagnostic.ide0055.severity = none # Formatting rule: Incompatible with CSharpier
+dotnet_diagnostic.ide0007.severity = none # Use var instead of explicit type: Preference
+dotnet_diagnostic.ide0009.severity = none # Add this or Me qualification: Preference
+dotnet_diagnostic.ide0200.severity = none # Remove unnecessary lambda expression: may be performance reasons not to
+dotnet_diagnostic.ide0058.severity = none # Remove unnecessary expression value: Subjective
+dotnet_diagnostic.ide0010.severity = none # Add missing cases to switch statement: Too verbose
+dotnet_diagnostic.ide0200.severity = none # Remove unnecessary lambda expression: may be performance reasons not to
+dotnet_diagnostic.ide0058.severity = none # Remove unnecessary expression value: Subjective
+dotnet_diagnostic.ide0305.severity = none # Use collection expression for fluent: Can obfuscate intent
+dotnet_diagnostic.ide0001.severity = suggestion # Name can be simplified: Non enforceable in build
+dotnet_diagnostic.ide0046.severity = suggestion # Use conditional expression for return: Subjective
+dotnet_diagnostic.ide0045.severity = suggestion # Use conditional expression for assignment: Subjective
+dotnet_diagnostic.ide0078.severity = suggestion # Use pattern matching: Subjective
+dotnet_diagnostic.ide0260.severity = suggestion # Use pattern matching: Subjective
+dotnet_diagnostic.ide0022.severity = suggestion # Use expression body for method: Subjective
+dotnet_diagnostic.ide0061.severity = suggestion # Use expression body for local functions: Subjective
+dotnet_diagnostic.ide0063.severity = suggestion # Using directive can be simplified
+dotnet_diagnostic.ide0066.severity = suggestion # Use switch expression: Subjective
+dotnet_diagnostic.ide0029.severity = suggestion # Null check can be simplified: Subjective
+dotnet_diagnostic.ide0030.severity = suggestion # Null check can be simplified: Subjective
+dotnet_diagnostic.ide0270.severity = suggestion # Null check can be simplified: Subjective
+dotnet_diagnostic.ide0042.severity = suggestion # Deconstruct variable declaration: Subjective
+dotnet_diagnostic.ide0039.severity = suggestion # Use local function instead of lambda: Subjective
+dotnet_diagnostic.ide0029.severity = suggestion # Null check can be simplified: Subjective
+dotnet_diagnostic.ide0030.severity = suggestion # Null check can be simplified: Subjective
+dotnet_diagnostic.ide0270.severity = suggestion # Null check can be simplified: Subjective
+dotnet_diagnostic.ide0042.severity = suggestion # Deconstruct variable declaration: Subjective
+dotnet_diagnostic.ide0028.severity = suggestion # Use collection initializers: Subjective
+dotnet_diagnostic.ide0072.severity = suggestion # Populate switch statement: Subjective
+dotnet_diagnostic.ide0074.severity = suggestion # Use compound assignment: Subjective
+dotnet_diagnostic.ide0300.severity = suggestion # Use collection expression for array: Subjective, maybe aspirational
+dotnet_diagnostic.ide0290.severity = suggestion # primary constructors: subjective, and readonly properties are not a thing
+dotnet_diagnostic.ide0290.severity = suggestion # Use primary constructor: Subjective
+dotnet_diagnostic.ide0037.severity = suggestion # Use inferred member names: Sometimes its nice to be explicit
+dotnet_diagnostic.ide0301.severity = suggestion # Use collection expression for empty: Subjective, intent
+dotnet_diagnostic.ide0021.severity = suggestion # Use expression body for constructors : Subjective
+dotnet_diagnostic.ide0090.severity = suggestion # Simplify new expression : Subjective
+
+dotnet_diagnostic.ide0047.severity = suggestion # Parentheses preferences: IDEs don't properly pick it up
+dotnet_diagnostic.ide0130.severity = suggestion # Namespace does not match folder structure : Aspirational
+dotnet_diagnostic.ide1006.severity = suggestion # Naming rule violation : Aspirational
+
+# Maintainability rules
+dotnet_diagnostic.ca1501.severity = warning # Avoid excessive inheritance
+dotnet_diagnostic.ca1502.severity = warning # Avoid excessive complexity
+dotnet_diagnostic.ca1505.severity = warning # Avoid unmaintainable code
+dotnet_diagnostic.ca1506.severity = warning # Avoid excessive class coupling
+dotnet_diagnostic.ca1507.severity = warning # Use nameof in place of string
+dotnet_diagnostic.ca1508.severity = warning # Avoid dead conditional code
+dotnet_diagnostic.ca1509.severity = warning # Invalid entry in code metrics configuration file
+dotnet_diagnostic.ca1861.severity = suggestion # Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861)
+
+
+# Performance rules
+dotnet_diagnostic.ca1849.severity = suggestion # Call async methods when in an async method: May decrease performance
+dotnet_diagnostic.ca1822.severity = suggestion # Mark member as static
+dotnet_diagnostic.ca1859.severity = suggestion # Use concrete types when possible for improved performance
+
+# Design rule
+dotnet_diagnostic.ca1002.severity = suggestion # Do not expose generic lists
+dotnet_diagnostic.ca1051.severity = warning # Do not declare visible instance fields
+dotnet_diagnostic.ca1056.severity = suggestion # URI properties should not be strings
+dotnet_diagnostic.ca1062.severity = none # Public method must check all parameters for null
+
+# Naming
+dotnet_diagnostic.ca1707.severity = none # Remove underscores in names
+
+# Usage
+dotnet_diagnostic.ca2227.severity = suggestion # Collection props should be read-only
+
+dotnet_code_quality.ca1051.exclude_structs = true # CA1051 is excluded in structs
+dotnet_code_quality.dispose_ownership_transfer_at_constructor = true # CA2000 has a lot of false positives without this
+dotnet_code_quality.dispose_ownership_transfer_at_method_call = true # CA2000 has a lot of false positives without this
+dotnet_code_quality.dispose_analysis_kind = NonExceptionPathsOnlyNotDisposed # CA2000 has a lot of false positives without this
+
+# NUnit
+dotnet_diagnostic.NUnit2001.severity = warning # Consider using Assert.That(expr, Is.False) instead of Assert.False(expr)
+dotnet_diagnostic.NUnit2002.severity = warning # Consider using Assert.That(expr, Is.False) instead of Assert.IsFalse(expr)
+dotnet_diagnostic.NUnit2003.severity = warning # Consider using Assert.That(expr, Is.True) instead of Assert.IsTrue(expr)
+dotnet_diagnostic.NUnit2004.severity = warning # Consider using Assert.That(expr, Is.True) instead of Assert.True(expr)
+dotnet_diagnostic.NUnit2005.severity = warning # Consider using Assert.That(actual, Is.EqualTo(expected)) instead of Assert.AreEqual(expected, actual)
+dotnet_diagnostic.NUnit2006.severity = warning # Consider using Assert.That(actual, Is.Not.EqualTo(expected)) instead of Assert.AreNotEqual(expected, actual)
+
+dotnet_diagnostic.NUnit2010.severity = warning # Use EqualConstraint for better assertion messages in case of failure
+dotnet_diagnostic.NUnit2011.severity = warning # Use ContainsConstraint for better assertion messages in case of failure
+dotnet_diagnostic.NUnit2011.severity = warning # Use StartsWithConstraint for better assertion messages in case of failure
+dotnet_diagnostic.NUnit2011.severity = warning # Use EndsWithConstraint for better assertion messages in case of failure
+dotnet_diagnostic.NUnit2014.severity = warning # Use SomeItemsConstraint for better assertion messages in case of failure
+
+dotnet_diagnostic.NUnit2015.severity = warning # Consider using Assert.That(actual, Is.SameAs(expected)) instead of Assert.AreSame(expected, actual)
+dotnet_diagnostic.NUnit2016.severity = warning # Consider using Assert.That(expr, Is.Null) instead of Assert.Null(expr)
+dotnet_diagnostic.NUnit2017.severity = warning # Consider using Assert.That(expr, Is.Null) instead of Assert.IsNull(expr)
+dotnet_diagnostic.NUnit2018.severity = warning # Consider using Assert.That(expr, Is.Not.Null) instead of Assert.NotNull(expr)
+dotnet_diagnostic.NUnit2028.severity = warning # Consider using Assert.That(actual, Is.GreaterThanOrEqualTo(expected)) instead of Assert.GreaterOrEqual(actual, expected)
+dotnet_diagnostic.NUnit2027.severity = warning # Consider using Assert.That(actual, Is.GreaterThan(expected)) instead of Assert.Greater(actual, expected)
+dotnet_diagnostic.NUnit2029.severity = warning # Consider using Assert.That(actual, Is.LessThan(expected)) instead of Assert.Less(actual, expected)
+dotnet_diagnostic.NUnit2030.severity = warning # Consider using Assert.That(actual, Is.LessThanOrEqualTo(expected)) instead of Assert.LessOrEqual(actual, expected)
+dotnet_diagnostic.NUnit2031.severity = warning # Consider using Assert.That(actual, Is.Not.SameAs(expected)) instead of Assert.AreNotSame(expected, actual)
+dotnet_diagnostic.NUnit2032.severity = warning # Consider using Assert.That(expr, Is.Zero) instead of Assert.Zero(expr)
+dotnet_diagnostic.NUnit2033.severity = warning # Consider using Assert.That(expr, Is.Not.Zero) instead of Assert.NotZero(expr)
+dotnet_diagnostic.NUnit2034.severity = warning # Consider using Assert.That(expr, Is.NaN) instead of Assert.IsNaN(expr)
+dotnet_diagnostic.NUnit2035.severity = warning # Consider using Assert.That(collection, Is.Empty) instead of Assert.IsEmpty(collection)
+dotnet_diagnostic.NUnit2036.severity = warning # Consider using Assert.That(collection, Is.Not.Empty) instead of Assert.IsNotEmpty(collection)
+dotnet_diagnostic.NUnit2037.severity = warning # Consider using Assert.That(collection, Does.Contain(instance)) instead of Assert.Contains(instance, collection)
+dotnet_diagnostic.NUnit2038.severity = warning # Consider using Assert.That(actual, Is.InstanceOf(expected)) instead of Assert.IsInstanceOf(expected, actual)
+dotnet_diagnostic.NUnit2039.severity = warning # Consider using Assert.That(actual, Is.Not.InstanceOf(expected)) instead of Assert.IsNotInstanceOf(expected, actual)
+
+[*.{appxmanifest,asax,ascx,aspx,axaml,build,c,c++,cc,cginc,compute,cp,cpp,cs,cshtml,cu,cuh,cxx,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,nuspec,paml,razor,resw,resx,shader,skin,tpp,usf,ush,vb,xaml,xamlx,xoml,xsd}]
+indent_style = space
+indent_size = 2
+tab_width = 2
+
+# Verify
+[*.{received,verified}.{json}]
+charset = utf-8-bom
+end_of_line = lf
+indent_size = unset
+indent_style = unset
+insert_final_newline = false
+tab_width = unset
+trim_trailing_whitespace = false
\ No newline at end of file
diff --git a/.github/workflows/close-issue.yml b/.github/workflows/close-issue.yml
deleted file mode 100644
index 21a1d7a..0000000
--- a/.github/workflows/close-issue.yml
+++ /dev/null
@@ -1,77 +0,0 @@
-name: Update issue Status
-
-on:
- issues:
- types: [closed]
-
-jobs:
- update_issue:
- runs-on: ubuntu-latest
- steps:
- - name: Get project data
- env:
- GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
- ORGANIZATION: specklesystems
- PROJECT_NUMBER: 9
- run: |
- gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
- query($org: String!, $number: Int!) {
- organization(login: $org){
- projectNext(number: $number) {
- id
- fields(first:20) {
- nodes {
- id
- name
- settings
- }
- }
- }
- }
- }' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json
-
- echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
- echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
-
- echo "$PROJECT_ID"
- echo "$STATUS_FIELD_ID"
-
- echo 'DONE_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .settings | fromjson | .options[] | select(.name== "Done") | .id' project_data.json) >> $GITHUB_ENV
- echo "$DONE_ID"
-
- - name: Add Issue to project #it's already in the project, but we do this to get its node id!
- env:
- GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
- ISSUE_ID: ${{ github.event.issue.node_id }}
- run: |
- item_id="$( gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
- mutation($project:ID!, $id:ID!) {
- addProjectNextItem(input: {projectId: $project, contentId: $id}) {
- projectNextItem {
- id
- }
- }
- }' -f project=$PROJECT_ID -f id=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
-
- echo 'ITEM_ID='$item_id >> $GITHUB_ENV
-
- - name: Update Status
- env:
- GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
- ISSUE_ID: ${{ github.event.issue.node_id }}
- run: |
- gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
- mutation($project:ID!, $status:ID!, $id:ID!, $value:String!) {
- set_status: updateProjectNextItemField(
- input: {
- projectId: $project
- itemId: $id
- fieldId: $status
- value: $value
- }
- ) {
- projectNextItem {
- id
- }
- }
- }' -f project=$PROJECT_ID -f status=$STATUS_FIELD_ID -f id=$ITEM_ID -f value=${{ env.DONE_ID }}
diff --git a/.github/workflows/open-issue.yml b/.github/workflows/open-issue.yml
deleted file mode 100644
index 0c0943c..0000000
--- a/.github/workflows/open-issue.yml
+++ /dev/null
@@ -1,50 +0,0 @@
-name: Move new issues into Project
-
-on:
- issues:
- types: [opened]
-
-jobs:
- track_issue:
- runs-on: ubuntu-latest
- steps:
- - name: Get project data
- env:
- GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
- ORGANIZATION: specklesystems
- PROJECT_NUMBER: 9
- run: |
- gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
- query($org: String!, $number: Int!) {
- organization(login: $org){
- projectNext(number: $number) {
- id
- fields(first:20) {
- nodes {
- id
- name
- settings
- }
- }
- }
- }
- }' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json
-
- echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
- echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
-
- - name: Add Issue to project
- env:
- GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
- ISSUE_ID: ${{ github.event.issue.node_id }}
- run: |
- item_id="$( gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
- mutation($project:ID!, $id:ID!) {
- addProjectNextItem(input: {projectId: $project, contentId: $id}) {
- projectNextItem {
- id
- }
- }
- }' -f project=$PROJECT_ID -f id=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
-
- echo 'ITEM_ID='$item_id >> $GITHUB_ENV
diff --git a/.github/workflows/open-ssie.yml b/.github/workflows/open-ssie.yml
deleted file mode 100644
index 831d2b0..0000000
--- a/.github/workflows/open-ssie.yml
+++ /dev/null
@@ -1,50 +0,0 @@
-name: Move new issues into Project
-
-on:
- issues:
- types: [opened]
-
-jobs:
- track_issue:
- runs-on: ubuntu-latest
- steps:
- - name: Get project data
- env:
- GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
- ORGANIZATION: specklesystems
- PROJECT_NUMBER: 9
- run: |
- gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
- query($org: String!, $number: Int!) {
- organization(login: $org){
- projectNext(number: $number) {
- id
- fields(first:20) {
- nodes {
- id
- name
- settings
- }
- }
- }
- }
- }' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json
-
- echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
- echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
-
- - name: Add Issue to project
- env:
- GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
- ISSUE_ID: ${{ github.event.issue.node_id }}
- run: |
- item_id="$( gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
- mutation($project:ID!, $id:ID!) {
- addProjectNextItem(input: {projectId: $project, contentId: $id}) {
- projectNextItem {
- id
- }
- }
- }' -f project=$PROJECT_ID -f id=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
-
- echo 'ITEM_ID='$item_id >> $GITHUB_ENV
diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml
new file mode 100644
index 0000000..ffb0e48
--- /dev/null
+++ b/.github/workflows/workflow.yml
@@ -0,0 +1,49 @@
+name: .NET Build and Publish
+
+on:
+ pull_request:
+ push:
+ tags: ["v*.*.*"]
+
+jobs:
+ build:
+ env:
+ SOLUTION_NAME: "GrasshopperAsyncComponent.sln"
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.x.x
+
+ - id: set-version
+ name: Set version to output
+ run: |
+ TAG=${{ github.ref_name }}
+ if [[ "${{ github.ref }}" != refs/tags/* ]]; then
+ TAG="v0.0.99.${{ github.run_number }}"
+ fi
+ SEMVER="${TAG#v}"
+ FILE_VERSION=$(echo "$TAG" | sed -E 's/^v([0-9]+\.[0-9]+\.[0-9]+).*/\1/')
+ FILE_VERSION="$FILE_VERSION.${{ github.run_number }}"
+
+ echo "semver=$SEMVER" >> "$GITHUB_OUTPUT"
+ echo "fileVersion=$FILE_VERSION" >> "$GITHUB_OUTPUT"
+
+ echo $SEMVER
+ echo $FILE_VERSION
+ - name: restore
+ run: dotnet restore ${{env.SOLUTION_NAME}}
+
+ - name: build
+ run: dotnet build ${{env.SOLUTION_NAME}} --configuration release --no-restore -warnaserror -p:Version=${{steps.set-version.outputs.semver}} -p:FileVersion=${{steps.set-version.outputs.fileVersion}}
+
+ - name: pack
+ run: dotnet pack ${{env.SOLUTION_NAME}} --no-build -p:Version=${{steps.set-version.outputs.semver}} -p:FileVersion=${{steps.set-version.outputs.fileVersion}}
+
+ - name: Push to nuget.org
+ if: (github.ref_type == 'tag')
+ run: dotnet nuget push output/*.nupkg --source "https://api.nuget.org/v3/index.json" --api-key ${{secrets.CONNECTORS_NUGET_TOKEN }} --skip-duplicate
diff --git a/CodeMetricsConfig.txt b/CodeMetricsConfig.txt
new file mode 100644
index 0000000..e69bf23
--- /dev/null
+++ b/CodeMetricsConfig.txt
@@ -0,0 +1,4 @@
+CA1502: 25
+CA1501: 5
+CA1506(Method): 50
+CA1506(Type): 95
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..b1b69d9
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,69 @@
+
+
+ 12
+ enable
+ enable
+
+
+
+
+
+ Speckle
+ Copyright (c) AEC Systems Ltd
+ https://speckle.systems/
+ README.md
+ https://github.com/specklesystems/GrasshopperAsyncComponent
+ git
+ speckle
+ Apache-2.0
+
+
+ false
+
+ true
+ true
+ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
+
+
+ true
+
+
+ true
+ latest-AllEnabledByDefault
+ true
+ true
+ false
+ true
+
+
+
+ CA5399;CA1812;
+
+ CS1591;CS1573;
+
+ CA1303;CA1304;CA1305;CA1307;CA1308;CA1309;CA1310;CA1311;
+
+ CA1848;CA1727;
+
+ CA1815;CA1725;
+
+ CA1710;CA1711;CA1720;CA1724;
+
+ CA1502;CA1716;NETSDK1206;
+ $(NoWarn)
+
+
+
+
+ $(MSBuildThisFileDirectory)
+
+
+
+
+
+
+
+
+
diff --git a/GrasshopperAsyncComponent.sln b/GrasshopperAsyncComponent.sln
index 6d573ca..293d009 100644
--- a/GrasshopperAsyncComponent.sln
+++ b/GrasshopperAsyncComponent.sln
@@ -7,6 +7,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrasshopperAsyncComponent",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrasshopperAsyncComponentDemo", "GrasshopperAsyncComponentDemo\GrasshopperAsyncComponentDemo.csproj", "{695D2B91-DDB6-416E-8A99-DDE6253DA7AA}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{B84A4AEC-CAA4-4065-89A4-E7FBB3889D25}"
+ ProjectSection(SolutionItems) = preProject
+ Directory.Build.props = Directory.Build.props
+ .editorconfig = .editorconfig
+ .csharpierrc.yaml = .csharpierrc.yaml
+ CodeMetricsConfig.txt = CodeMetricsConfig.txt
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
diff --git a/GrasshopperAsyncComponent/GH_AsyncComponent.cs b/GrasshopperAsyncComponent/GH_AsyncComponent.cs
index 3178cd3..60a6681 100755
--- a/GrasshopperAsyncComponent/GH_AsyncComponent.cs
+++ b/GrasshopperAsyncComponent/GH_AsyncComponent.cs
@@ -1,249 +1,267 @@
-using Grasshopper.Kernel;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
+using System.Collections.Concurrent;
using System.Diagnostics;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
+using Grasshopper.Kernel;
using Timer = System.Timers.Timer;
-namespace GrasshopperAsyncComponent
-{
- ///
- /// Inherit your component from this class to make all the async goodness available.
- ///
- public abstract class GH_AsyncComponent : GH_Component
- {
- public override Guid ComponentGuid => throw new Exception("ComponentGuid should be overriden in any descendant of GH_AsyncComponent!");
+namespace GrasshopperAsyncComponent;
+///
+/// Inherit your component from this class to make all the async goodness available.
+///
+public abstract class GH_AsyncComponent : GH_Component, IDisposable
+{
//List<(string, GH_RuntimeMessageLevel)> Errors;
- Action ReportProgress;
+ private readonly Action _reportProgress;
- public ConcurrentDictionary ProgressReports;
+ public ConcurrentDictionary ProgressReports { get; protected set; }
- Action Done;
+ private readonly Action _done;
- Timer DisplayProgressTimer;
+ private readonly Timer _displayProgressTimer;
- int State = 0;
+ private int _state;
- int SetData = 0;
+ private int _setData;
- public List Workers;
+ public List Workers { get; protected set; }
- List Tasks;
+ private readonly List _tasks;
- public readonly List CancellationSources;
+ public List CancellationSources { get; }
///
- /// Set this property inside the constructor of your derived component.
+ /// Set this property inside the constructor of your derived component.
///
- public WorkerInstance BaseWorker { get; set; }
+ public WorkerInstance? BaseWorker { get; set; }
///
/// Optional: if you have opinions on how the default system task scheduler should treat your workers, set it here.
///
- public TaskCreationOptions? TaskCreationOptions { get; set; } = null;
+ public TaskCreationOptions? TaskCreationOptions { get; set; }
- protected GH_AsyncComponent(string name, string nickname, string description, string category, string subCategory) : base(name, nickname, description, category, subCategory)
+ protected GH_AsyncComponent(string name, string nickname, string description, string category, string subCategory)
+ : base(name, nickname, description, category, subCategory)
{
+ Workers = new List();
+ CancellationSources = new List();
+ _tasks = new List();
+ ProgressReports = new ConcurrentDictionary();
- DisplayProgressTimer = new Timer(333) { AutoReset = false };
- DisplayProgressTimer.Elapsed += DisplayProgress;
+ _displayProgressTimer = new Timer(333) { AutoReset = false };
+ _displayProgressTimer.Elapsed += DisplayProgress;
- ReportProgress = (id, value) =>
- {
- ProgressReports[id] = value;
- if (!DisplayProgressTimer.Enabled)
+ _reportProgress = (id, value) =>
{
- DisplayProgressTimer.Start();
- }
- };
+ ProgressReports[id] = value;
+ if (!_displayProgressTimer.Enabled)
+ {
+ _displayProgressTimer.Start();
+ }
+ };
- Done = () =>
- {
- Interlocked.Increment(ref State);
- if (State == Workers.Count && SetData == 0)
+ _done = () =>
{
- Interlocked.Exchange(ref SetData, 1);
+ Interlocked.Increment(ref _state);
+ if (_state == Workers.Count && _setData == 0)
+ {
+ Interlocked.Exchange(ref _setData, 1);
- // We need to reverse the workers list to set the outputs in the same order as the inputs.
- Workers.Reverse();
+ // We need to reverse the workers list to set the outputs in the same order as the inputs.
+ Workers.Reverse();
- Rhino.RhinoApp.InvokeOnUiThread((Action)delegate
- {
- ExpireSolution(true);
- });
- }
- };
-
- ProgressReports = new ConcurrentDictionary();
-
- Workers = new List();
- CancellationSources = new List();
- Tasks = new List();
+ Rhino.RhinoApp.InvokeOnUiThread(
+ (Action)
+ delegate
+ {
+ ExpireSolution(true);
+ }
+ );
+ }
+ };
}
public virtual void DisplayProgress(object sender, System.Timers.ElapsedEventArgs e)
{
- if (Workers.Count == 0 || ProgressReports.Values.Count == 0)
- {
- return;
- }
-
- if (Workers.Count == 1)
- {
- Message = ProgressReports.Values.Last().ToString("0.00%");
- }
- else
- {
- double total = 0;
- foreach (var kvp in ProgressReports)
+ if (Workers.Count == 0 || ProgressReports.Values.Count == 0)
{
- total += kvp.Value;
+ return;
}
- Message = (total / Workers.Count).ToString("0.00%");
- }
+ if (Workers.Count == 1)
+ {
+ Message = ProgressReports.Values.Last().ToString("0.00%");
+ }
+ else
+ {
+ double total = 0;
+ foreach (var kvp in ProgressReports)
+ {
+ total += kvp.Value;
+ }
- Rhino.RhinoApp.InvokeOnUiThread((Action)delegate
- {
- OnDisplayExpired(true);
- });
+ Message = (total / Workers.Count).ToString("0.00%");
+ }
+
+ Rhino.RhinoApp.InvokeOnUiThread(
+ (Action)
+ delegate
+ {
+ OnDisplayExpired(true);
+ }
+ );
}
protected override void BeforeSolveInstance()
{
- if (State != 0 && SetData == 1)
- {
- return;
- }
+ if (_state != 0 && _setData == 1)
+ {
+ return;
+ }
- Debug.WriteLine("Killing");
+ Debug.WriteLine("Killing");
- foreach (var source in CancellationSources)
- {
- source.Cancel();
- }
+ foreach (var source in CancellationSources)
+ {
+ source.Cancel();
+ }
- CancellationSources.Clear();
- Workers.Clear();
- ProgressReports.Clear();
- Tasks.Clear();
+ CancellationSources.Clear();
+ Workers.Clear();
+ ProgressReports.Clear();
+ _tasks.Clear();
- Interlocked.Exchange(ref State, 0);
+ Interlocked.Exchange(ref _state, 0);
}
protected override void AfterSolveInstance()
{
- System.Diagnostics.Debug.WriteLine("After solve instance was called " + State + " ? " + Workers.Count);
- // We need to start all the tasks as close as possible to each other.
- if (State == 0 && Tasks.Count > 0 && SetData == 0)
- {
- System.Diagnostics.Debug.WriteLine("After solve INVOKATIONM");
- foreach (var task in Tasks)
+ System.Diagnostics.Debug.WriteLine("After solve instance was called " + _state + " ? " + Workers.Count);
+ // We need to start all the tasks as close as possible to each other.
+ if (_state == 0 && _tasks.Count > 0 && _setData == 0)
{
- task.Start();
+ System.Diagnostics.Debug.WriteLine("After solve INVOKATION");
+ foreach (var task in _tasks)
+ {
+ task.Start();
+ }
}
- }
}
protected override void ExpireDownStreamObjects()
{
- // Prevents the flash of null data until the new solution is ready
- if (SetData == 1)
- {
- base.ExpireDownStreamObjects();
- }
+ // Prevents the flash of null data until the new solution is ready
+ if (_setData == 1)
+ {
+ base.ExpireDownStreamObjects();
+ }
}
- protected override void SolveInstance(IGH_DataAccess DA)
+ protected override void SolveInstance(IGH_DataAccess da)
{
- //return;
- if (State == 0)
- {
- if (BaseWorker == null)
+ //return;
+ if (_state == 0)
{
- AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Worker class not provided.");
- return;
+ if (BaseWorker == null)
+ {
+ AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Worker class not provided.");
+ return;
+ }
+
+ // Add cancellation source to our bag
+ var tokenSource = new CancellationTokenSource();
+ CancellationSources.Add(tokenSource);
+
+ var currentWorker = BaseWorker.Duplicate($"Worker-{da.Iteration}", tokenSource.Token);
+ if (currentWorker == null)
+ {
+ AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Could not get a worker instance.");
+ return;
+ }
+
+ // Let the worker collect data.
+ currentWorker.GetData(da, Params);
+
+ var currentRun =
+ TaskCreationOptions != null
+ ? new Task(
+ () => currentWorker.DoWork(_reportProgress, _done),
+ tokenSource.Token,
+ (TaskCreationOptions)TaskCreationOptions
+ )
+ : new Task(() => currentWorker.DoWork(_reportProgress, _done), tokenSource.Token);
+
+ // Add the worker to our list
+ Workers.Add(currentWorker);
+
+ _tasks.Add(currentRun);
+
+ return;
}
- var currentWorker = BaseWorker.Duplicate();
- if (currentWorker == null)
+ if (_setData == 0)
{
- AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Could not get a worker instance.");
- return;
+ return;
}
- // Let the worker collect data.
- currentWorker.GetData(DA, Params);
+ if (Workers.Count > 0)
+ {
+ Interlocked.Decrement(ref _state);
+ Workers[_state].SetData(da);
+ }
- // Create the task
- var tokenSource = new CancellationTokenSource();
- currentWorker.CancellationToken = tokenSource.Token;
- currentWorker.Id = $"Worker-{DA.Iteration}";
+ if (_state != 0)
+ {
+ return;
+ }
- var currentRun = TaskCreationOptions != null
- ? new Task(() => currentWorker.DoWork(ReportProgress, Done), tokenSource.Token, (TaskCreationOptions)TaskCreationOptions)
- : new Task(() => currentWorker.DoWork(ReportProgress, Done), tokenSource.Token);
+ CancellationSources.Clear();
+ Workers.Clear();
+ ProgressReports.Clear();
+ _tasks.Clear();
- // Add cancellation source to our bag
- CancellationSources.Add(tokenSource);
+ Interlocked.Exchange(ref _setData, 0);
- // Add the worker to our list
- Workers.Add(currentWorker);
-
- Tasks.Add(currentRun);
-
- return;
- }
-
- if (SetData == 0)
- {
- return;
- }
-
- if (Workers.Count > 0)
- {
- Interlocked.Decrement(ref State);
- Workers[State].SetData(DA);
- }
-
- if (State != 0)
- {
- return;
- }
-
- CancellationSources.Clear();
- Workers.Clear();
- ProgressReports.Clear();
- Tasks.Clear();
-
- Interlocked.Exchange(ref SetData, 0);
-
- Message = "Done";
- OnDisplayExpired(true);
+ Message = "Done";
+ OnDisplayExpired(true);
}
public void RequestCancellation()
{
- foreach (var source in CancellationSources)
- {
- source.Cancel();
- }
+ foreach (var source in CancellationSources)
+ {
+ source.Cancel();
+ }
- CancellationSources.Clear();
- Workers.Clear();
- ProgressReports.Clear();
- Tasks.Clear();
+ CancellationSources.Clear();
+ Workers.Clear();
+ ProgressReports.Clear();
+ _tasks.Clear();
- Interlocked.Exchange(ref State, 0);
- Interlocked.Exchange(ref SetData, 0);
- Message = "Cancelled";
- OnDisplayExpired(true);
+ Interlocked.Exchange(ref _state, 0);
+ Interlocked.Exchange(ref _setData, 0);
+ Message = "Cancelled";
+ OnDisplayExpired(true);
}
- }
+ private bool _isDisposed;
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ _isDisposed = true;
+
+ if (disposing)
+ {
+ _displayProgressTimer?.Dispose();
+ }
+ }
+ }
}
diff --git a/GrasshopperAsyncComponent/GrasshopperAsyncComponent.csproj b/GrasshopperAsyncComponent/GrasshopperAsyncComponent.csproj
index df8c1ba..21e9a93 100755
--- a/GrasshopperAsyncComponent/GrasshopperAsyncComponent.csproj
+++ b/GrasshopperAsyncComponent/GrasshopperAsyncComponent.csproj
@@ -1,33 +1,25 @@
-
+
net48
- 10
GrasshopperAsyncComponent
GrasshopperAsyncComponent
-
-
+
+
+ GrasshopperAsyncComponent
Grasshopper Async Component
Jankless Grasshopper Components
- Speckle Systems
- Speckle Systems
- Copyright AEC Systems © 2020, 2021
+ grasshopper rhino mcneel gh_component
{114D5E49-AC13-47F7-A70E-B4289579F4E3}
-
-
- GrasshopperAsyncComponent
- false
- Apache-2.0
- https://github.com/specklesystems/GrasshopperAsyncComponent
- grasshopper rhino mcneel gh_component
+
+ true
true
snupkg
- true
-
-
+
+
@@ -39,4 +31,4 @@
C:\Program Files\Rhino 7\System\Rhino.exe
Program
-
\ No newline at end of file
+
diff --git a/GrasshopperAsyncComponent/WorkerInstance.cs b/GrasshopperAsyncComponent/WorkerInstance.cs
index e633c93..cd21e0c 100755
--- a/GrasshopperAsyncComponent/WorkerInstance.cs
+++ b/GrasshopperAsyncComponent/WorkerInstance.cs
@@ -1,66 +1,47 @@
using Grasshopper.Kernel;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-namespace GrasshopperAsyncComponent
+namespace GrasshopperAsyncComponent;
+
+///
+/// A class that holds the actual compute logic and encapsulates the state it needs. Every needs to have one.
+///
+public abstract class WorkerInstance(GH_Component? parent, string id, CancellationToken cancellationToken)
{
-
- ///
- /// A class that holds the actual compute logic and encapsulates the state it needs. Every needs to have one.
- ///
- public abstract class WorkerInstance
- {
-
///
/// The parent component. Useful for passing state back to the host component.
///
- public GH_Component Parent { get; set; }
-
- ///
- /// This token is set by the parent .
- ///
- public CancellationToken CancellationToken { get; set; }
+ public GH_Component? Parent { get; set; } = parent;
- ///
- /// This is set by the parent . You can set it yourself, but it's not really worth it.
- ///
- public string Id { get; set; }
+ public CancellationToken CancellationToken { get; } = cancellationToken;
- protected WorkerInstance(GH_Component _parent)
- {
- Parent = _parent;
- }
+ public string Id { get; set; } = id;
///
/// This is a "factory" method. It should return a fresh instance of this class, but with all the necessary state that you might have passed on directly from your component.
///
+ /// A Unique id for the new duplicate
+ /// A cancellationToken to be passed to the new duplicate
///
- public abstract WorkerInstance Duplicate();
+ public abstract WorkerInstance? Duplicate(string id, CancellationToken cancellationToken);
///
- /// This method is where the actual calculation/computation/heavy lifting should be done.
- /// Make sure you always check as frequently as you can if is cancelled. For an example, see the .
+ /// This method is where the actual calculation/computation/heavy lifting should be done.
+ /// Make sure you always check as frequently as you can if is cancelled. For an example, see the PrimeCalculatorWorker example.
///
- /// Call this to report progress up to the parent component.
- /// Call this when everything is done. It will tell the parent component that you're ready to .
- public abstract void DoWork(Action ReportProgress, Action Done);
+ /// Call this to report progress up to the parent component.
+ /// Call this when everything is done. It will tell the parent component that you're ready to .
+ public abstract void DoWork(Action reportProgress, Action done);
///
- /// Write your data setting logic here. Do not call this function directly from this class. It will be invoked by the parent after you've called `Done` in the function.
+ /// Write your data setting logic here. Do not call this function directly from this class. It will be invoked by the parent after you've called `Done` in the function.
///
- ///
- public abstract void SetData(IGH_DataAccess DA);
+ ///
+ public abstract void SetData(IGH_DataAccess da);
///
/// Write your data collection logic here. Do not call this method directly. It will be invoked by the parent .
///
- ///
- ///
- public abstract void GetData(IGH_DataAccess DA, GH_ComponentParamServer Params);
- }
-
+ ///
+ ///
+ public abstract void GetData(IGH_DataAccess da, GH_ComponentParamServer parameters);
}
diff --git a/GrasshopperAsyncComponentDemo/GrasshopperAsyncComponentDemo.csproj b/GrasshopperAsyncComponentDemo/GrasshopperAsyncComponentDemo.csproj
index 50bf0c3..23a994e 100644
--- a/GrasshopperAsyncComponentDemo/GrasshopperAsyncComponentDemo.csproj
+++ b/GrasshopperAsyncComponentDemo/GrasshopperAsyncComponentDemo.csproj
@@ -9,6 +9,7 @@
GrasshopperAsyncComponentDemo
GrasshopperAsyncComponentDemo
true
+ en
@@ -28,4 +29,4 @@
C:\Program Files\Rhino 7\System\Rhino.exe
Program
-
\ No newline at end of file
+
diff --git a/GrasshopperAsyncComponentDemo/Info/GrasshopperAsyncComponentInfo.cs b/GrasshopperAsyncComponentDemo/Info/GrasshopperAsyncComponentInfo.cs
index c69295a..a3dda17 100644
--- a/GrasshopperAsyncComponentDemo/Info/GrasshopperAsyncComponentInfo.cs
+++ b/GrasshopperAsyncComponentDemo/Info/GrasshopperAsyncComponentInfo.cs
@@ -1,57 +1,21 @@
-using System;
-using System.Drawing;
-using Grasshopper.Kernel;
+using Grasshopper.Kernel;
-namespace GrasshopperAsyncComponentDemo
+namespace GrasshopperAsyncComponentDemo;
+
+public class GrasshopperAsyncComponentInfo : GH_AssemblyInfo
{
- public class GrasshopperAsyncComponentInfo : GH_AssemblyInfo
- {
- public override string Name
- {
- get
- {
- return "GrasshopperAsyncComponentDemo";
- }
- }
- public override Bitmap Icon
- {
- get
- {
- //Return a 24x24 pixel bitmap to represent this GHA library.
- return null;
- }
- }
- public override string Description
- {
- get
- {
- //Return a short string describing the purpose of this GHA library.
- return "A base for async & less janky grasshopper components.";
- }
- }
- public override Guid Id
- {
- get
- {
- return new Guid("9c8808bc-ddee-45ca-8c66-05ca3cf4d394");
- }
- }
+ public override string Name => "GrasshopperAsyncComponentDemo";
- public override string AuthorName
- {
- get
- {
- //Return a string identifying you or your company.
- return "";
- }
- }
- public override string AuthorContact
- {
- get
- {
- //Return a string representing your preferred contact details.
- return "";
- }
- }
- }
+ //Return a short string describing the purpose of this GHA library.
+
+ public override string Description => "A base for async & less janky grasshopper components.";
+
+ public override Guid Id => new("9c8808bc-ddee-45ca-8c66-05ca3cf4d394");
+
+ //Return a string identifying you or your company.
+
+ public override string AuthorName => "";
+
+ //Return a string representing your preferred contact details.
+ public override string AuthorContact => "";
}
diff --git a/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_PrimeCalculatorAsyncComponent.cs b/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_PrimeCalculatorAsyncComponent.cs
index cb79606..e1bacb0 100755
--- a/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_PrimeCalculatorAsyncComponent.cs
+++ b/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_PrimeCalculatorAsyncComponent.cs
@@ -1,110 +1,143 @@
-using Grasshopper.Kernel;
-using System;
-using System.Windows.Forms;
+using System.Windows.Forms;
+using Grasshopper.Kernel;
using GrasshopperAsyncComponent;
-namespace GrasshopperAsyncComponentDemo.SampleImplementations
+namespace GrasshopperAsyncComponentDemo.SampleImplementations;
+
+public class Sample_PrimeCalculatorAsyncComponent : GH_AsyncComponent
{
- public class Sample_PrimeCalculatorAsyncComponent : GH_AsyncComponent
+ public override Guid ComponentGuid => new Guid("22C612B0-2C57-47CE-B9FE-E10621F18933");
+
+ protected override System.Drawing.Bitmap Icon => Properties.Resources.logo32;
+
+ public override GH_Exposure Exposure => GH_Exposure.primary;
+
+ public Sample_PrimeCalculatorAsyncComponent()
+ : base("The N-th Prime Calculator", "PRIME", "Calculates the nth prime number.", "Samples", "Async")
{
- public override Guid ComponentGuid { get => new Guid("22C612B0-2C57-47CE-B9FE-E10621F18933"); }
+ BaseWorker = new PrimeCalculatorWorker(this);
+ }
- protected override System.Drawing.Bitmap Icon { get => Properties.Resources.logo32; }
+ protected override void RegisterInputParams(GH_InputParamManager pManager)
+ {
+ pManager.AddIntegerParameter(
+ "N",
+ "N",
+ "Which n-th prime number. Minimum 1, maximum one million. Take care, it can burn your CPU.",
+ GH_ParamAccess.item
+ );
+ }
- public override GH_Exposure Exposure => GH_Exposure.primary;
+ protected override void RegisterOutputParams(GH_OutputParamManager pManager)
+ {
+ pManager.AddNumberParameter("Output", "O", "The n-th prime number.", GH_ParamAccess.item);
+ }
- public Sample_PrimeCalculatorAsyncComponent() : base("The N-th Prime Calculator", "PRIME", "Calculates the nth prime number.", "Samples", "Async")
- {
- BaseWorker = new PrimeCalculatorWorker();
- }
-
- protected override void RegisterInputParams(GH_InputParamManager pManager)
- {
- pManager.AddIntegerParameter("N", "N", "Which n-th prime number. Minimum 1, maximum one million. Take care, it can burn your CPU.", GH_ParamAccess.item);
- }
-
- protected override void RegisterOutputParams(GH_OutputParamManager pManager)
- {
- pManager.AddNumberParameter("Output", "O", "The n-th prime number.", GH_ParamAccess.item);
- }
-
- public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
- {
- base.AppendAdditionalMenuItems(menu);
- Menu_AppendItem(menu, "Cancel", (s, e) =>
+ public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
+ {
+ base.AppendAdditionalMenuItems(menu);
+ Menu_AppendItem(
+ menu,
+ "Cancel",
+ (s, e) =>
{
RequestCancellation();
- });
- }
+ }
+ );
+ }
- private class PrimeCalculatorWorker : WorkerInstance
+ private sealed class PrimeCalculatorWorker : WorkerInstance
+ {
+ private int TheNthPrime { get; set; } = 100;
+ private long ThePrime { get; set; } = -1;
+
+ public PrimeCalculatorWorker(
+ GH_Component? parent,
+ string id = "baseworker",
+ CancellationToken cancellationToken = default
+ )
+ : base(parent, id, cancellationToken) { }
+
+ public override void DoWork(Action reportProgress, Action done)
{
- int TheNthPrime { get; set; } = 100;
- long ThePrime { get; set; } = -1;
+ // 👉 Checking for cancellation!
+ if (CancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
- public PrimeCalculatorWorker() : base(null) { }
+ int count = 0;
+ long a = 2;
- public override void DoWork(Action ReportProgress, Action Done)
+ // Thanks Steak Overflow (TM) https://stackoverflow.com/a/13001749/
+ while (count < TheNthPrime)
{
// 👉 Checking for cancellation!
- if (CancellationToken.IsCancellationRequested) { return; }
-
- int count = 0;
- long a = 2;
-
- // Thanks Steak Overflow (TM) https://stackoverflow.com/a/13001749/
- while (count < TheNthPrime)
+ if (CancellationToken.IsCancellationRequested)
{
- // 👉 Checking for cancellation!
- if (CancellationToken.IsCancellationRequested) { return; }
-
- long b = 2;
- int prime = 1;// to check if found a prime
- while (b * b <= a)
- {
- // 👉 Checking for cancellation!
- if (CancellationToken.IsCancellationRequested) { return; }
-
- if (a % b == 0)
- {
- prime = 0;
- break;
- }
- b++;
- }
-
- ReportProgress(Id, ((double)count) / TheNthPrime);
-
- if (prime > 0)
- {
- count++;
- }
- a++;
+ return;
}
- ThePrime = --a;
- Done();
+ long b = 2;
+ int prime = 1; // to check if found a prime
+ while (b * b <= a)
+ {
+ // 👉 Checking for cancellation!
+ if (CancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ if (a % b == 0)
+ {
+ prime = 0;
+ break;
+ }
+ b++;
+ }
+
+ reportProgress(Id, (double)count / TheNthPrime);
+
+ if (prime > 0)
+ {
+ count++;
+ }
+ a++;
}
- public override WorkerInstance Duplicate() => new PrimeCalculatorWorker();
+ ThePrime = --a;
+ done();
+ }
- public override void GetData(IGH_DataAccess DA, GH_ComponentParamServer Params)
+ public override WorkerInstance Duplicate(string id, CancellationToken cancellationToken) =>
+ new PrimeCalculatorWorker(Parent, id, cancellationToken);
+
+ public override void GetData(IGH_DataAccess da, GH_ComponentParamServer parameters)
+ {
+ int nthPrime = 100;
+ da.GetData(0, ref nthPrime);
+ if (nthPrime > 1000000)
{
- int _nthPrime = 100;
- DA.GetData(0, ref _nthPrime);
- if (_nthPrime > 1000000) _nthPrime = 1000000;
- if (_nthPrime < 1) _nthPrime = 1;
-
- TheNthPrime = _nthPrime;
+ nthPrime = 1000000;
}
- public override void SetData(IGH_DataAccess DA)
+ if (nthPrime < 1)
{
- // 👉 Checking for cancellation!
- if (CancellationToken.IsCancellationRequested) { return; }
-
- DA.SetData(0, ThePrime);
+ nthPrime = 1;
}
+
+ TheNthPrime = nthPrime;
+ }
+
+ public override void SetData(IGH_DataAccess da)
+ {
+ // 👉 Checking for cancellation!
+ if (CancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ da.SetData(0, ThePrime);
}
}
}
diff --git a/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_UslessCyclesComponent.cs b/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_UslessCyclesComponent.cs
index bccce9f..6fc0466 100755
--- a/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_UslessCyclesComponent.cs
+++ b/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_UslessCyclesComponent.cs
@@ -1,89 +1,115 @@
-using Grasshopper.Kernel;
-using System;
-using System.Threading;
-using System.Windows.Forms;
+using System.Windows.Forms;
+using Grasshopper.Kernel;
using GrasshopperAsyncComponent;
+namespace GrasshopperAsyncComponentDemo.SampleImplementations;
-namespace GrasshopperAsyncComponentDemo.SampleImplementations
+public class Sample_UselessCyclesAsyncComponent : GH_AsyncComponent
{
- public class Sample_UselessCyclesAsyncComponent : GH_AsyncComponent
+ public override Guid ComponentGuid => new Guid("DF2B93E2-052D-4BE4-BC62-90DC1F169BF6");
+
+ protected override System.Drawing.Bitmap Icon => Properties.Resources.logo32;
+
+ public override GH_Exposure Exposure => GH_Exposure.primary;
+
+ public Sample_UselessCyclesAsyncComponent()
+ : base("Sample Async Component", "CYCLOMATRON-X", "Spins uselessly.", "Samples", "Async")
{
- public override Guid ComponentGuid { get => new Guid("DF2B93E2-052D-4BE4-BC62-90DC1F169BF6"); }
+ BaseWorker = new UselessCyclesWorker(this);
+ }
- protected override System.Drawing.Bitmap Icon { get => Properties.Resources.logo32; }
+ private sealed class UselessCyclesWorker(
+ GH_Component? parent,
+ string id = "baseworker",
+ CancellationToken cancellationToken = default
+ ) : WorkerInstance(parent, id, cancellationToken)
+ {
+ private int MaxIterations { get; set; } = 100;
- public override GH_Exposure Exposure => GH_Exposure.primary;
-
- public Sample_UselessCyclesAsyncComponent() : base("Sample Async Component", "CYCLOMATRON-X", "Spins uselessly.", "Samples", "Async")
+ public override void DoWork(Action reportProgress, Action done)
{
- BaseWorker = new UselessCyclesWorker();
- }
-
- private class UselessCyclesWorker : WorkerInstance
- {
- int MaxIterations { get; set; } = 100;
-
- public UselessCyclesWorker() : base(null) { }
-
- public override void DoWork(Action ReportProgress, Action Done)
+ // Checking for cancellation
+ if (CancellationToken.IsCancellationRequested)
{
- // Checking for cancellation
- if (CancellationToken.IsCancellationRequested) { return; }
+ return;
+ }
- for (int i = 0; i <= MaxIterations; i++)
+ for (int i = 0; i <= MaxIterations; i++)
+ {
+ var sw = new SpinWait();
+ for (int j = 0; j <= 100; j++)
{
- var sw = new SpinWait();
- for (int j = 0; j <= 100; j++)
- sw.SpinOnce();
-
- ReportProgress(Id, ((double)(i + 1) / (double)MaxIterations));
-
- // Checking for cancellation
- if (CancellationToken.IsCancellationRequested) { return; }
+ sw.SpinOnce();
}
- Done();
+ reportProgress(Id, (i + 1) / (double)MaxIterations);
+
+ // Checking for cancellation
+ if (CancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
}
- public override WorkerInstance Duplicate() => new UselessCyclesWorker();
+ done();
+ }
- public override void GetData(IGH_DataAccess DA, GH_ComponentParamServer Params)
+ public override WorkerInstance Duplicate(string id, CancellationToken cancellationToken) =>
+ new UselessCyclesWorker(Parent, id, cancellationToken);
+
+ public override void GetData(IGH_DataAccess da, GH_ComponentParamServer parameters)
+ {
+ if (CancellationToken.IsCancellationRequested)
{
- if (CancellationToken.IsCancellationRequested) return;
-
- int _maxIterations = 100;
- DA.GetData(0, ref _maxIterations);
- if (_maxIterations > 1000) _maxIterations = 1000;
- if (_maxIterations < 10) _maxIterations = 10;
-
- MaxIterations = _maxIterations;
+ return;
}
- public override void SetData(IGH_DataAccess DA)
+ int maxIterations = 100;
+ da.GetData(0, ref maxIterations);
+ if (maxIterations > 1000)
{
- if (CancellationToken.IsCancellationRequested) return;
- DA.SetData(0, $"Hello world. Worker {Id} has spun for {MaxIterations} iterations.");
+ maxIterations = 1000;
}
- }
- protected override void RegisterInputParams(GH_InputParamManager pManager)
- {
- pManager.AddIntegerParameter("N", "N", "Number of spins.", GH_ParamAccess.item);
- }
-
- protected override void RegisterOutputParams(GH_OutputParamManager pManager)
- {
- pManager.AddTextParameter("Output", "O", "Nothing really interesting.", GH_ParamAccess.item);
- }
-
- public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
- {
- base.AppendAdditionalMenuItems(menu);
- Menu_AppendItem(menu, "Cancel", (s, e) =>
+ if (maxIterations < 10)
{
- RequestCancellation();
- });
+ maxIterations = 10;
+ }
+
+ MaxIterations = maxIterations;
+ }
+
+ public override void SetData(IGH_DataAccess da)
+ {
+ if (CancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ da.SetData(0, $"Hello world. Worker {Id} has spun for {MaxIterations} iterations.");
}
}
+
+ protected override void RegisterInputParams(GH_InputParamManager pManager)
+ {
+ pManager.AddIntegerParameter("N", "N", "Number of spins.", GH_ParamAccess.item);
+ }
+
+ protected override void RegisterOutputParams(GH_OutputParamManager pManager)
+ {
+ pManager.AddTextParameter("Output", "O", "Nothing really interesting.", GH_ParamAccess.item);
+ }
+
+ public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
+ {
+ base.AppendAdditionalMenuItems(menu);
+ Menu_AppendItem(
+ menu,
+ "Cancel",
+ (s, e) =>
+ {
+ RequestCancellation();
+ }
+ );
+ }
}