Compare commits
475 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 786e683d89 | |||
| 8d999f4f9c | |||
| bb7542e254 | |||
| d6f6254a92 | |||
| f60f85b639 | |||
| bcdf73cc70 | |||
| 47e72ee1a7 | |||
| f3de5324db | |||
| 4dd6db886f | |||
| 4b82db8ea2 | |||
| 9e7f26f7a6 | |||
| b19f8c4219 | |||
| c517e61517 | |||
| b3e0623856 | |||
| e5d1ef2448 | |||
| 83c3de05fa | |||
| 507ded7d4a | |||
| e15029bab3 | |||
| a43fd44206 | |||
| 1bcd8ac3a4 | |||
| a8dc93e22b | |||
| 5a0f883b98 | |||
| a5d035671a | |||
| cd6ebad619 | |||
| 33c2e6e1a4 | |||
| b97702adb1 | |||
| 80c4f694ec | |||
| fb5042004f | |||
| c0a9291632 | |||
| b783d2acb6 | |||
| 93539adc1e | |||
| 98005933de | |||
| 50906b172a | |||
| 05f7353925 | |||
| 8328498553 | |||
| 59019bf846 | |||
| 3afaf61a1a | |||
| 424609fad0 | |||
| 46c067308e | |||
| 0e33e8df8f | |||
| bc81c21e9d | |||
| 7f8b59d348 | |||
| 44ba61e4a5 | |||
| 4f7b470901 | |||
| 8c6426d617 | |||
| 5562ce1a2d | |||
| 7019b8d7c6 | |||
| 58a0326060 | |||
| 55f83919d1 | |||
| 46c57b18be | |||
| 7b5ada57cd | |||
| e29b27bcd3 | |||
| ff1b688321 | |||
| 0be143d391 | |||
| c0a66a297a | |||
| aad604e819 | |||
| 48313cb082 | |||
| 0361a6e5b7 | |||
| 0e97782c29 | |||
| 298dedc3af | |||
| 422403d499 | |||
| b652ffa773 | |||
| 68ace02e2d | |||
| b6be7a351f | |||
| 1039e75d0c | |||
| 0f8752d5ab | |||
| 64a93345d6 | |||
| efc38d8f5c | |||
| e3ca75abe1 | |||
| b479d368ad | |||
| 8d3985f93b | |||
| 915a18dc98 | |||
| 5fcb3223d6 | |||
| 21851c06d2 | |||
| 3a9a633d30 | |||
| 7f092d529c | |||
| a4f0e0e4aa | |||
| 227729a0df | |||
| 178085f3f8 | |||
| 9794195e9c | |||
| a61c442930 | |||
| 68a407905d | |||
| 0f2abaf532 | |||
| 07634b6f6a | |||
| e938725d35 | |||
| d3369e3ce5 | |||
| d75a61d775 | |||
| 2ae4003afb | |||
| 24db4c4ae4 | |||
| edf63d4a1b | |||
| b5b0922e7f | |||
| ff390f772d | |||
| d69f0bba2a | |||
| 33c14fc14c | |||
| 536e58aacc | |||
| 88188aace6 | |||
| ad44a7cdbc | |||
| 38449dca9a | |||
| 764eb43838 | |||
| a84e6d89ca | |||
| 377829adae | |||
| a479440b66 | |||
| cc9639b179 | |||
| d44b4fa52b | |||
| ea6ca8c555 | |||
| 113f0fd551 | |||
| bcc4e25970 | |||
| b733ce5f29 | |||
| 1c8b2b82d7 | |||
| 11cd2dc1cb | |||
| 3789898ea2 | |||
| 0b3318f9e1 | |||
| 7589ad5f05 | |||
| 6df32262bd | |||
| 001ca1c287 | |||
| 59e459559b | |||
| d305fe59cb | |||
| f163b2822e | |||
| 630bb38b8b | |||
| bacff2da34 | |||
| 129d5285ed | |||
| 5d07fe0ea0 | |||
| 062e3c2838 | |||
| f4cc4bc77e | |||
| d395f03219 | |||
| 56ed399d70 | |||
| 7e0766fc7f | |||
| 0df343ebe1 | |||
| 3cad57ceb3 | |||
| 11937ad7c9 | |||
| 8210fde69a | |||
| fa6f90621e | |||
| f6f1852664 | |||
| b3f4190614 | |||
| 2fc0024cd2 | |||
| 300a5627fd | |||
| 22f029fe33 | |||
| c728266c88 | |||
| 7f2d57cdad | |||
| 81a22bd4cc | |||
| f64da099ab | |||
| f4ba200640 | |||
| 4e84766be9 | |||
| 73a95aded0 | |||
| 404600a839 | |||
| 93d517eab7 | |||
| 4110d90107 | |||
| 2d134bf7e1 | |||
| 686d0fd31c | |||
| b06878bbe1 | |||
| 64114167a8 | |||
| 0c7cd75353 | |||
| 3c5679ad2a | |||
| b043623021 | |||
| bd0997913a | |||
| ac0ab3904c | |||
| 5c9d672a2b | |||
| a79f0fd035 | |||
| 18d6653282 | |||
| d58a656c8a | |||
| 720d049145 | |||
| 129688a646 | |||
| fa66258b23 | |||
| 9f0c572993 | |||
| 158baff5b0 | |||
| 7a29d27e46 | |||
| 82e3d37dd1 | |||
| 9695ec8c51 | |||
| 15fa319433 | |||
| 793bbb9cd3 | |||
| 3291010d43 | |||
| 14732ce174 | |||
| ba655988b0 | |||
| 96822c4e66 | |||
| 08356de1ad | |||
| 375f5071ae | |||
| 5900a3c178 | |||
| d7bf324029 | |||
| c784fbf462 | |||
| 26f1802787 | |||
| a96e0f8c8e | |||
| 9f36c9cfe5 | |||
| 4959f277e8 | |||
| ea08b83f7a | |||
| b24dc685fa | |||
| 7cad14fe25 | |||
| 4d552b6834 | |||
| 378a91995e | |||
| 53b66dd26b | |||
| 9dd04c0881 | |||
| 474c18c29f | |||
| b676da4ac4 | |||
| fe964f4c8e | |||
| 1fea4cc01b | |||
| c3d716f6fd | |||
| 236cca2663 | |||
| f20064c9f0 | |||
| 6f1b22d13a | |||
| 23f95dd38d | |||
| 9eb767a4f6 | |||
| 358680cb9a | |||
| 87ae033f61 | |||
| e5a09155a2 | |||
| 988599fbb5 | |||
| baa5f54edb | |||
| d580fd0bc1 | |||
| 2352306269 | |||
| f0bafee076 | |||
| ef19bfa69d | |||
| 45601f1a2f | |||
| adf3298baf | |||
| 8f5e5f675b | |||
| 9217e67a9d | |||
| fe68607a52 | |||
| 88d15a5c9c | |||
| 3072047129 | |||
| 316379b04c | |||
| b056d98645 | |||
| 7329155d7a | |||
| c9a5103da7 | |||
| dbdf4892db | |||
| 3aa993cecb | |||
| 327f05adca | |||
| ec0d4bf1e3 | |||
| 73afa28026 | |||
| f0c7169be8 | |||
| ee10e9d3fc | |||
| 93912d6712 | |||
| f81fc97a91 | |||
| cc23c147be | |||
| 02892a96fe | |||
| ba143fe44b | |||
| 3a71a10cf1 | |||
| bafd130ece | |||
| f79ae7e058 | |||
| 77170bf9f3 | |||
| 69167dc206 | |||
| 19a5ccfb74 | |||
| b3c6a75387 | |||
| c6556fa2ef | |||
| 3bc40fb0bb | |||
| 4370af5376 | |||
| 912971d8de | |||
| 9318d85cc8 | |||
| deddb216e5 | |||
| 465d69ba00 | |||
| 14d959834f | |||
| 465f635142 | |||
| ed5bdc91ed | |||
| 0023d2ca2a | |||
| ab487991b6 | |||
| 142eefdf39 | |||
| ebccf6ff79 | |||
| 11fe8e8cce | |||
| 1fe1a54dcf | |||
| a1b9030dba | |||
| eec400d0cf | |||
| 991b31265f | |||
| 675d896e0d | |||
| defcee165a | |||
| 722df50d34 | |||
| 431bb459fc | |||
| a393f7ca8b | |||
| 650fad3b22 | |||
| 81007e0ecf | |||
| d29450f0a9 | |||
| 1e5bf903a2 | |||
| 878edf7c8f | |||
| ab0ff7b792 | |||
| b5999e3361 | |||
| d904c68c10 | |||
| daec64cf8e | |||
| 1844386235 | |||
| 05f90eae5c | |||
| 7bbc568022 | |||
| 7a1edbbf99 | |||
| 058e3cb946 | |||
| 08dd45cc32 | |||
| d4173c1449 | |||
| 4b7c6e4641 | |||
| 2477423ba7 | |||
| 71ec9a6d11 | |||
| 140b39f605 | |||
| 1a0522cef3 | |||
| b343d9bd0d | |||
| e32ec52071 | |||
| 2bbe2a8b93 | |||
| 604e141468 | |||
| 39dcc5da0b | |||
| af35edf0a2 | |||
| fbc7400524 | |||
| 8a71b19f78 | |||
| 105395e9fb | |||
| 715bb7274a | |||
| 43445bc4ff | |||
| 3bff2d5d57 | |||
| 4784e7d432 | |||
| 378149a0af | |||
| 0240e3b49e | |||
| d65993389b | |||
| 4cc78c4bc9 | |||
| 8a148b892f | |||
| fba0c46ed8 | |||
| 91c4090037 | |||
| c2962a668b | |||
| 27d3028880 | |||
| 93b3c37584 | |||
| f5a8d1a738 | |||
| e10cc38ca7 | |||
| b6d2d01068 | |||
| cca8828565 | |||
| 3c783b3cac | |||
| 8f8be7482a | |||
| 3b859ba398 | |||
| 4c52072e0a | |||
| 1241589d18 | |||
| 1a9d28e660 | |||
| e14913b8c3 | |||
| 0d7805d060 | |||
| 14359333b4 | |||
| 75d88c5f39 | |||
| 9a3eddf6d5 | |||
| f541525a80 | |||
| c3e8a95bf1 | |||
| c2d0e1d0d4 | |||
| f0d83d8d43 | |||
| ba14a8c87f | |||
| 065a2318d3 | |||
| 40d025bb57 | |||
| ddeac27f04 | |||
| d73bf365c2 | |||
| 198f1a2564 | |||
| 694e9e1915 | |||
| 0f29aacf24 | |||
| a28cf6d908 | |||
| 2cb5d6e410 | |||
| 59628d5241 | |||
| bbe23f6996 | |||
| df5b151c90 | |||
| ccb4f54550 | |||
| 30a3533956 | |||
| e07bfc18d5 | |||
| 2ad14ca00b | |||
| 450dbcce81 | |||
| 296a00e2eb | |||
| f085a3fb5a | |||
| 56c4109210 | |||
| 92bcb51bf2 | |||
| ce3d610ac5 | |||
| 1be044e442 | |||
| cc7d274f43 | |||
| 0b1de566df | |||
| 5e0ea324c3 | |||
| b9180027c5 | |||
| 77a40a3af5 | |||
| 22f4945d71 | |||
| c0f8949705 | |||
| 7883cd9d32 | |||
| b7be9b6fb1 | |||
| 2b46792a82 | |||
| 4c3e572bfe | |||
| 0f116ad847 | |||
| 16b1b904af | |||
| 9916048921 | |||
| 9bea79b3b2 | |||
| af1d7dfaf3 | |||
| 03a5706680 | |||
| 42882cb71b | |||
| b14c8db8d3 | |||
| 2fec0ea791 | |||
| dc4da49078 | |||
| ea5ed874a3 | |||
| 70729d5ddd | |||
| e443840acf | |||
| d269ac73dd | |||
| 148a11f83e | |||
| 66b3c5e60c | |||
| cfc4018bd3 | |||
| 4b74ca8607 | |||
| 1c64a975c0 | |||
| 78faa71592 | |||
| 6f5f044095 | |||
| 0b240f0752 | |||
| 6d54be55db | |||
| 7148aa0a6f | |||
| f8e4682670 | |||
| 9da1d791cc | |||
| 77ffcead69 | |||
| a9a9b6b525 | |||
| adcf81b5c8 | |||
| fc46610b5b | |||
| 69fceb4d09 | |||
| 9671745fdb | |||
| 787ad92ff2 | |||
| e79716c5f0 | |||
| 1f92335d6d | |||
| 02f75c717c | |||
| ce3e591a47 | |||
| 059253ff39 | |||
| 957a284332 | |||
| 5c7a96b960 | |||
| 58911632e3 | |||
| 9e2f5d57de | |||
| 64267d7697 | |||
| ee94ac2bf9 | |||
| 5f8379806d | |||
| 580521f3b0 | |||
| 18ad5f5c11 | |||
| fcf4e223f7 | |||
| a16bc4eae0 | |||
| a15390ab0d | |||
| a2f0270a6e | |||
| 04446b9e79 | |||
| f408980a98 | |||
| b70a0c6fe3 | |||
| 7f50e9a09a | |||
| b52d8e0023 | |||
| e97597596a | |||
| 4536ecee87 | |||
| 72b7123f7b | |||
| 72fe3d1384 | |||
| 481a095ed3 | |||
| e3a5af037e | |||
| fe27951d96 | |||
| baf5ab0b14 | |||
| 057aef10c0 | |||
| c35ce07337 | |||
| e088e139af | |||
| dc62aa22b9 | |||
| cd67efdfff | |||
| f0124a76c1 | |||
| 5d4e14e802 | |||
| a88f860c5e | |||
| 1112ec39ab | |||
| 3ec1007b7b | |||
| 82d51af262 | |||
| e83c0125ba | |||
| b23e91651c | |||
| db88dbe6d0 | |||
| da792173ff | |||
| 51f0c26838 | |||
| cb14c43381 | |||
| ea68a245b8 | |||
| c5df445483 | |||
| adadaa4d91 | |||
| 6e46fd2f8a | |||
| 5df0c4401e | |||
| 6778b7086c | |||
| 2dcf89f6fe | |||
| 3249265560 | |||
| 200b84f49a | |||
| 1268a08baf | |||
| 46c3f9d1ac | |||
| 29e7e84f4e | |||
| b2883c91e4 | |||
| 82b7ca1a26 | |||
| b56eac2f50 | |||
| f26f79c3c8 | |||
| a0b723b2cb | |||
| 1b472be6b7 | |||
| 662984268b | |||
| 1d681f9166 | |||
| cfe5f330c9 | |||
| 17e6780357 | |||
| 98132d2811 | |||
| d9d1865504 | |||
| 405f3f4ee4 | |||
| ca350002ba | |||
| 4ea9e4b9aa | |||
| 2269182922 | |||
| d7dbd3349f | |||
| ebec2c5bd2 | |||
| a74ead9bd7 | |||
| f46aa4f50b | |||
| afde379cda |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"csharpier": {
|
||||
"version": "1.0.2",
|
||||
"commands": [
|
||||
"csharpier"
|
||||
],
|
||||
"rollForward": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.*
|
||||
@@ -0,0 +1,7 @@
|
||||
printWidth: 120
|
||||
useTabs: false
|
||||
indentSize: 2
|
||||
preprocessorSymbolSets:
|
||||
- ""
|
||||
- "DEBUG"
|
||||
- "DEBUG,CODE_STYLE"
|
||||
+327
@@ -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
|
||||
@@ -0,0 +1,8 @@
|
||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||
* text=auto
|
||||
|
||||
# need original files to be windows
|
||||
*.txt text eol=crlf
|
||||
|
||||
# Verify
|
||||
*.verified.json text eol=lf working-tree-encoding=UTF-8
|
||||
@@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at hello@speckle.systems. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
@@ -0,0 +1,58 @@
|
||||
# Speckle Contribution Guidelines
|
||||
|
||||
## Introduction
|
||||
|
||||
Thank you for reading this! Speckle's a rather wide network of parts that depend on each other, either directly, indirectly or even just cosmetically.
|
||||
|
||||
> **Speckle** is a quite large ecosystem of moving parts. Any changes may have unintended effects, that can cause problems quickly for many people (and processes) that rely on Speckle.
|
||||
|
||||
This means that what might look like a simple quick change in one repo may have a big hidden cost that propagates around other parts of the project. We're all here to help each other, and this guide is meant to help you get started and promote a framework that can untangle all these dependecies through discussion!
|
||||
|
||||
## Bugs & Issues 🐞
|
||||
|
||||
### Found a new bug?
|
||||
|
||||
- First step is to check whether this is a new bug! We encourage you to search through the issues of the project in question **and** associated repos!
|
||||
|
||||
- If you come up with nothing, **open a new issue with a clear title and description**, as much relevant information as possible: system configuration, code samples & steps to reproduce the problem.
|
||||
|
||||
- Can't mention this often enough: tells us how to reproduce the problem! We will ignore or flag as such issues without reproduction steps.
|
||||
|
||||
- Try to reference & note all potentially affected projects.
|
||||
|
||||
### Sending a PR for Bug Fixes
|
||||
|
||||
You fixed something! Great! We hope you logged it first :) Make sure though that you've covered the lateral thinking needed for a bug report, as described above, also in your implementation! If there any tests, make sure they all pass. If there are none, it means they're missing - so add them!
|
||||
|
||||
### Code Style
|
||||
|
||||
When collaborating on a project in GitHub, it's important to follow coding conventions and style guidelines to ensure consistency and readability of the codebase. One commonly used convention is to use two spaces for indentation.
|
||||
|
||||
To use two spaces for indentation in GitHub, you can configure your text editor or IDE to use this indentation style. Once you've written your code, you can commit and push your changes to GitHub.
|
||||
|
||||
When collaborating with others on GitHub, it's important to communicate any changes to coding conventions or style guidelines, and to work together to maintain a consistent coding style throughout the project. Code reviews can also help ensure that code is well-formatted and easy to read.
|
||||
|
||||
## New Features 🎉
|
||||
|
||||
The golden rule is to Discuss First!
|
||||
|
||||
- Before embarking on adding a new feature, suggest it first as an issue with the `enhancement` label and/or title - this will allow relevant people to pitch in
|
||||
- We'll now discuss your requirements and see how and if they fit within the Speckle ecosystem.
|
||||
- The last step is to actually start writing code & submit a PR so we can follow along!
|
||||
- All new features should, if and where possible, come with tests. We won't merge without!
|
||||
|
||||
> Many clients may potentially have overlapping scopes, some features might already be in dev somewhere else, or might have been postponed to the next major release due to api instability in that area. For example, adding a delete stream button in the accounts panel in rhino: this feature was planned for speckle admin, and the whole functionality of the accounts panel in rhino is to be greatly reduced!
|
||||
|
||||
## Cosmetic Patches ✨
|
||||
|
||||
Changes that are cosmetic in nature and do not add anything substantial to the stability or functionality of Speckle **will generally not be accepted**.
|
||||
|
||||
Why? However trivial the changes might seem, there might be subtle reasons for the original code to be as it is. Furthermore, there are a lot of potential hidden costs (that even maintainers themselves are not aware of fully!) and they eat up review time unncessarily.
|
||||
|
||||
> **Examples**: modifying the colour of an UI element in one client may have a big hidden cost and need propagation in several other clients that implement a similar ui element. Changing the default port or specifiying `localhost` instead of `0.0.0.0` breaks cross-vm debugging and developing.
|
||||
|
||||
## Wrap up
|
||||
|
||||
Don't worry if you get things wrong. We all do, including project owners: this document should've been here a long time ago. There's plenty of room for discussion on our community [forum](https://discourse.speckle.works).
|
||||
|
||||
🙌❤️💙💚💜🙌
|
||||
@@ -0,0 +1,14 @@
|
||||
# Coding standards, domain knowledge, and preferences that AI should follow
|
||||
|
||||
## C# Coding Standards
|
||||
|
||||
- Use the csharpier formatter for formatting C# code.
|
||||
- Use the .editorconfig file for code style settings.
|
||||
- Always use `var` when the type is obvious from the right side of the assignment.
|
||||
- Always add braces for `if`, `else`, `for`, `foreach`, `while`, and `do` statements, even if they are single-line statements.
|
||||
|
||||
## Testing
|
||||
|
||||
- Use xUnit for unit testing.
|
||||
- Use FluentAssertions for assertions in tests.
|
||||
- Use Moq for mocking dependencies in tests.
|
||||
@@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions" # search for actions - there are other options available
|
||||
directory: "/" # search in .github/workflows under root `/`
|
||||
schedule:
|
||||
interval: "weekly" # check for action update every week
|
||||
@@ -0,0 +1,22 @@
|
||||
# Git Commit Instructions
|
||||
|
||||
To ensure high-quality and consistent commits, please follow these guidelines:
|
||||
|
||||
1. **Format your code**
|
||||
- Run the `csharpier` formatter on all C# files before committing.
|
||||
- Ensure your code adheres to the `.editorconfig` settings.
|
||||
|
||||
2. **Write clear commit messages**
|
||||
- Use the present tense ("Add feature" not "Added feature").
|
||||
- Start with a short summary (max 72 characters), followed by a blank line and a detailed description if necessary.
|
||||
|
||||
3. **Test your changes**
|
||||
- Run all unit tests before committing.
|
||||
- Add or update xUnit tests as needed.
|
||||
- Use AwesomeAssertions for assertions and Moq for mocking in tests.
|
||||
|
||||
4. **Review your changes**
|
||||
- Double-check for accidental debug code or commented-out code.
|
||||
- Ensure only relevant files are staged.
|
||||
|
||||
Thank you for helping maintain code quality!
|
||||
@@ -0,0 +1,46 @@
|
||||
name: .NET CI Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
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
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
|
||||
|
||||
- id: set-version
|
||||
name: Set version to output
|
||||
run: |
|
||||
SEMVER="3.0.99.${{ github.run_number }}"
|
||||
FILE_VERSION=$(echo "$SEMVER" | sed -E 's/^([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: 🔫 Build All
|
||||
run: ./build.sh
|
||||
env:
|
||||
SEMVER: ${{ steps.set-version.outputs.SEMVER }}
|
||||
FILE_VERSION: ${{ steps.set-version.outputs.FILE_VERSION }}
|
||||
|
||||
- name: Upload coverage reports to Codecov with GitHub Action
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
files: tests/**/coverage.xml
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
@@ -0,0 +1,54 @@
|
||||
name: .NET Build and Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["3.*"]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
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
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
|
||||
|
||||
- id: set-version
|
||||
name: Set version to output
|
||||
run: |
|
||||
TAG=${{ github.ref_name }}
|
||||
if [[ "${{ github.ref }}" != refs/tags/* ]]; then
|
||||
TAG="3.0.99.${{ github.run_number }}"
|
||||
fi
|
||||
SEMVER="${TAG}"
|
||||
FILE_VERSION=$(echo "$TAG" | sed -E 's/^([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: 🔫 Build and Pack
|
||||
run: ./build.sh pack
|
||||
env:
|
||||
SEMVER: ${{ steps.set-version.outputs.SEMVER }}
|
||||
FILE_VERSION: ${{ steps.set-version.outputs.FILE_VERSION }}
|
||||
|
||||
- name: Upload coverage reports to Codecov with GitHub Action
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
files: tests/**/coverage.xml
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
- name: Push to nuget.org
|
||||
run: dotnet nuget push output/*.nupkg --source "https://api.nuget.org/v3/index.json" --api-key ${{secrets.CONNECTORS_NUGET_TOKEN }} --skip-duplicate
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
**/bin/*
|
||||
**/obj/*
|
||||
_ReSharper.SharpCompress/
|
||||
bin/
|
||||
*.suo
|
||||
*.user
|
||||
TestArchives/Scratch/
|
||||
TestArchives/Scratch2/
|
||||
TestResults/
|
||||
*.nupkg
|
||||
packages/*/
|
||||
project.lock.json
|
||||
tests/TestArchives/Scratch
|
||||
.vs
|
||||
tools
|
||||
.vscode
|
||||
.idea/
|
||||
.volumes/
|
||||
|
||||
.DS_Store
|
||||
*.snupkg
|
||||
coverage.xml
|
||||
|
||||
*.received.*
|
||||
@@ -0,0 +1,50 @@
|
||||
# Speckle Contribution Guidelines
|
||||
|
||||
## Introduction
|
||||
|
||||
Thank you for reading this! Speckle's a rather wide network of parts that depend on each other, either directly, indirectly or even just cosmetically.
|
||||
|
||||
> **Speckle** is a quite large ecosystem of moving parts. Any changes may have unintended effects, that can cause problems quickly for many people (and processes) that rely on Speckle.
|
||||
|
||||
This means that what might look like a simple quick change in one repo may have a big hidden cost that propagates around other parts of the project. We're all here to help each other, and this guide is meant to help you get started and promote a framework that can untangle all these dependecies through discussion!
|
||||
|
||||
## Bugs & Issues 🐞
|
||||
|
||||
### Found a new bug?
|
||||
|
||||
- First step is to check whether this is a new bug! We encourage you to search through the issues of the project in question **and** associated repos!
|
||||
|
||||
- If you come up with nothing, **open a new issue with a clear title and description**, as much relevant information as possible: system configuration, code samples & steps to reproduce the problem.
|
||||
|
||||
- Can't mention this often enough: tells us how to reproduce the problem! We will ignore or flag as such issues without reproduction steps.
|
||||
|
||||
- Try to reference & note all potentially affected projects.
|
||||
|
||||
### Sending a PR for Bug Fixes
|
||||
|
||||
You fixed something! Great! We hope you logged it first :) Make sure though that you've covered the lateral thinking needed for a bug report, as described above, also in your implementation! If there any tests, make sure they all pass. If there are none, it means they're missing - so add them!
|
||||
|
||||
## New Features 🎉
|
||||
|
||||
The golden rule is to Discuss First!
|
||||
|
||||
- Before embarking on adding a new feature, suggest it first as an issue with the `enhancement` label and/or title - this will allow relevant people to pitch in
|
||||
- We'll now discuss your requirements and see how and if they fit within the Speckle ecosystem.
|
||||
- The last step is to actually start writing code & submit a PR so we can follow along!
|
||||
- All new features should, if and where possible, come with tests. We won't merge without!
|
||||
|
||||
> Many clients may potentially have overlapping scopes, some features might already be in dev somewhere else, or might have been postponed to the next major release due to api instability in that area. For example, adding a delete stream button in the accounts panel in rhino: this feature was planned for speckle admin, and the whole functionality of the accounts panel in rhino is to be greatly reduced!
|
||||
|
||||
## Cosmetic Patches ✨
|
||||
|
||||
Changes that are cosmetic in nature and do not add anything substantial to the stability or functionality of Speckle **will generally not be accepted**.
|
||||
|
||||
Why? However trivial the changes might seem, there might be subtle reasons for the original code to be as it is. Furthermore, there are a lot of potential hidden costs (that even maintainers themselves are not aware of fully!) and they eat up review time unncessarily.
|
||||
|
||||
> **Examples**: modifying the colour of an UI element in one client may have a big hidden cost and need propagation in several other clients that implement a similar ui element. Changing the default port or specifiying `localhost` instead of `0.0.0.0` breaks cross-vm debugging and developing.
|
||||
|
||||
## Wrap up
|
||||
|
||||
Don't worry if you get things wrong. We all do, including project owners: this document should've been here a long time ago. There's plenty of room for discussion on our community [forum](https://discourse.speckle.works).
|
||||
|
||||
🙌❤️💙💚💜🙌
|
||||
@@ -0,0 +1,4 @@
|
||||
CA1502: 25
|
||||
CA1501: 5
|
||||
CA1506(Method): 50
|
||||
CA1506(Type): 95
|
||||
@@ -0,0 +1,73 @@
|
||||
<Project>
|
||||
<PropertyGroup Label="Compiler Properties">
|
||||
<LangVersion>12</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Nugetspec Package Properties">
|
||||
<!-- Defines common Nugetspec properties -->
|
||||
<!-- Inheriting packable projects should define the rest of the nugetspec properties (PackageId, Description) -->
|
||||
<!-- and may, if needed, override/extend any of these (e.g. PackageTags) -->
|
||||
<Authors>Speckle</Authors>
|
||||
<Copyright>Copyright (c) AEC Systems Ltd</Copyright>
|
||||
<PackageProjectUrl>https://speckle.systems/</PackageProjectUrl>
|
||||
<PackageIcon>logo.png</PackageIcon>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<RepositoryUrl>https://github.com/specklesystems/speckle-sharp-sdk</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>speckle</PackageTags>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Nuget Package Properties">
|
||||
<IsPackable>false</IsPackable>
|
||||
<!--Can be set to true in inheriting .props/.csproj files for projects that should be packed-->
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
|
||||
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Analyers">
|
||||
<EnableNetAnalyzers>true</EnableNetAnalyzers>
|
||||
<AnalysisLevel>latest-AllEnabledByDefault</AnalysisLevel>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<!-- Ingored warnings, some aspirational but too noisy for now, some by design. -->
|
||||
<NoWarn>
|
||||
<!--Disabled by design-->
|
||||
CA5399;CA1812;
|
||||
<!--XML comment-->
|
||||
CS1591;CS1573;
|
||||
<!-- Globalization rules -->
|
||||
CA1303;CA1304;CA1305;CA1307;CA1308;CA1309;CA1310;CA1311;
|
||||
<!-- Logging -->
|
||||
CA1848;CA1727;
|
||||
<!-- Others we don't want -->
|
||||
CA1815;CA1725;
|
||||
<!-- Naming things is hard enough -->
|
||||
CA1710;CA1711;CA1720;CA1724;
|
||||
<!-- Aspirational -->
|
||||
CA1502;CA1716;NETSDK1206;
|
||||
$(NoWarn)
|
||||
</NoWarn
|
||||
>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Expose the repository root to all projects -->
|
||||
<RepositoryRoot>$(MSBuildThisFileDirectory)</RepositoryRoot>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
|
||||
<None Condition="'$(IsPackable)' == 'true'" Include="..\..\logo.png" Pack="true" PackagePath="\" Visible="false" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<!-- This file contains the configuration for some analyzer warnings, such as cyclomatic
|
||||
complexity threshold -->
|
||||
<AdditionalFiles Include="$(RepositoryRoot)CodeMetricsConfig.txt" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,17 @@
|
||||
<Project>
|
||||
<PropertyGroup Condition="'$(IsTestProject)' == 'true'">
|
||||
<NoWarn>
|
||||
<!-- Things we need to test -->
|
||||
CS0618;CA1034;CA2201;CA1051;CA1040;CA1724;
|
||||
IDE0044;IDE0130;CA1508;
|
||||
<!-- Analysers that provide no tangeable value to a test project -->
|
||||
CA5394;CA2007;CA1852;CA1819;CA1711;CA1063;CA1816;CA2234;CS8618;CA1054;CA1810;CA2208;CA1019;CA1831;
|
||||
$(NoWarn);
|
||||
</NoWarn>
|
||||
</PropertyGroup>
|
||||
<Target Name="DeepClean">
|
||||
<Message Text="Deep clean of $(MSBuildProjectName).csproj" Importance="high" />
|
||||
<RemoveDir Directories="$(BaseIntermediateOutputPath)" />
|
||||
<RemoveDir Directories="$(BaseOutputPath)" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,42 @@
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="altcover" Version="9.0.1" />
|
||||
<PackageVersion Include="AwesomeAssertions" Version="8.1.0" />
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
|
||||
<PackageVersion Include="Bullseye" Version="6.0.0" />
|
||||
<PackageVersion Include="GraphQL.Client" Version="6.0.0" />
|
||||
<PackageVersion Include="Glob" Version="1.1.9" />
|
||||
<PackageVersion Include="HttpMultipartParser" Version="9.0.0" />
|
||||
<PackageVersion Include="ILRepack.FullAuto" Version="1.6.0" />
|
||||
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<!-- Keep at exactly 7.0.5 for side by side with V2 -->
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="[7.0.5,)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.ObjectPool" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="[2.2.0,)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="[2.2.0,)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="[2.2.0,)" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="[5.0.0,)" />
|
||||
<PackageVersion Include="Moq" Version="4.20.72" />
|
||||
<PackageVersion Include="Newtonsoft.Json.Schema" Version="4.0.1" />
|
||||
<PackageVersion Include="Open.ChannelExtensions" Version="9.1.0" />
|
||||
<PackageVersion Include="Polly" Version="7.2.3" />
|
||||
<PackageVersion Include="Polly.Contrib.WaitAndRetry" Version="1.1.1" />
|
||||
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
|
||||
<PackageVersion Include="RichardSzalay.MockHttp" Version="7.0.0" />
|
||||
<PackageVersion Include="Speckle.Newtonsoft.Json" Version="13.0.2" />
|
||||
<PackageVersion Include="Speckle.DoubleNumerics" Version="4.1.0" />
|
||||
<PackageVersion Include="SimpleExec" Version="12.0.0" />
|
||||
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageVersion Include="System.Threading.Channels" Version="9.0.4" />
|
||||
<PackageVersion Include="Verify.Quibble" Version="2.1.1" />
|
||||
<PackageVersion Include="Verify.Xunit" Version="29.4.0" />
|
||||
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
|
||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.assert" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
|
||||
<GlobalPackageReference Include="PolySharp" Version="1.15.0" />
|
||||
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
||||
<GlobalPackageReference Include="Speckle.InterfaceGenerator" Version="0.9.6" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,17 @@
|
||||
If it's your first time here - or you forgot about them - make sure you read the [contribution guidelines](CONTRIBUTING.md), and then feel free to delete this line!
|
||||
|
||||
### Expected vs. Actual Behavior
|
||||
|
||||
Describe the problem here.
|
||||
|
||||
### Reproduction Steps & System Config (win, osx, web, etc.)
|
||||
|
||||
Let us know how we can reproduce this, and attach relevant files (if any).
|
||||
|
||||
### Proposed Solution (if any)
|
||||
|
||||
Let us know what how you would solve this.
|
||||
|
||||
#### Optional: Affected Projects
|
||||
|
||||
Does this issue propagate to other dependencies or dependents? If so, list them here!
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2020 AEC Systems
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,2 +1,88 @@
|
||||
# speckle-sharp-sdk
|
||||
The speckle core
|
||||

|
||||
Speckle | Sharp | SDK
|
||||
=================================================================================================================================
|
||||
|
||||
[](https://twitter.com/SpeckleSystems) [](https://speckle.community) [](https://speckle.systems) [](https://speckle.guide/dev/)
|
||||
|
||||
> Speckle is the first AEC data hub that connects with your favorite AEC tools. Speckle exists to overcome the challenges of working in a fragmented industry where communication, creative workflows, and the exchange of data are often hindered by siloed software and processes. It is here to make the industry better.
|
||||
|
||||
### .NET SDK, Tests, and Objects
|
||||
|
||||
[](https://codecov.io/gh/specklesystems/speckle-sharp-sdk)
|
||||
<a href="https://www.nuget.org/packages/Speckle.Sdk/"><img alt="NuGet Version" src="https://img.shields.io/nuget/v/Speckle.Sdk?label=Speckle.Sdk"></a>
|
||||
<a href="https://www.nuget.org/packages/Speckle.Objects/"><img alt="NuGet Version" src="https://img.shields.io/nuget/v/Speckle.Sdk?label=Speckle.Objects"></a>
|
||||
<a href="https://www.nuget.org/packages/Speckle.Automate.Sdk/"><img alt="NuGet Version" src="https://img.shields.io/nuget/v/Speckle.Sdk?label=Speckle.Automate.Sdk"></a>
|
||||
|
||||
> [!WARNING]
|
||||
> Releases Speckle.Sdk and Speckle.Objects are reliable for production use, but the APIs may not be wholly stable, and there may be breaking changes between releases, with little documentation.
|
||||
|
||||
# Repo structure
|
||||
|
||||
This repo is the home of our next-generation Speckle .NET SDK. It uses .NET Standard 2.0 and has been tested on Windows and MacOS.
|
||||
|
||||
- **SDK**
|
||||
- [`Speckle.Sdk`](https://github.com/specklesystems/speckle-sharp-sdk/tree/dev/src/Speckle.Sdk): Send/Receive operations, Serialization, API wrappers, and more!.
|
||||
- [`Speckle.Sdk.Dependencies`](https://github.com/specklesystems/speckle-sharp-sdk/tree/dev/src/Speckle.Sdk.Dependencies): Dependencies and code that shouldn't cause conflicts in Host Apps. This uses [IL Repack](https://github.com/gluck/il-repack) to merge together and interalized only to be used by Speckle.
|
||||
- [`Speckle.Automate.Sdk`](https://github.com/specklesystems/speckle-sharp-sdk/tree/dev/src/Speckle.Automate.Sdk): .NET SDK for [Speckle Automate](https://www.speckle.systems/product/automate)
|
||||
- **Speckle Objects**
|
||||
- [`Speckle.Objects`](https://github.com/specklesystems/speckle-sharp-sdk/tree/dev/src/Speckle.Objects): The Speckle Objects classes used for conversions.
|
||||
- **Tests**
|
||||
- [`Tests`](https://github.com/specklesystems/speckle-sharp-sdk/tree/dev/tests): Unit, serialization, integration, and performance tests.
|
||||
|
||||
### Other repos
|
||||
|
||||
Make sure to also check and ⭐️ these other repositories:
|
||||
|
||||
- [`speckle-sharp-connectors`](https://github.com/specklesystems/speckle-sharp-connectors): our csharp repo of next gen connectors.
|
||||
- [`speckle-server`](https://github.com/specklesystems/speckle-server): the speckle server.
|
||||
- [`speckle-sketchup`](https://github.com/specklesystems/speckle-blender): Blender connector.
|
||||
- [`speckle-sketchup`](https://github.com/specklesystems/speckle-sketchup): Sketchup connector.
|
||||
- [`speckle-powerbi`](https://github.com/specklesystems/speckle-powerbi): PowerBi connector.
|
||||
- and more [connectors & tooling](https://github.com/specklesystems/)!
|
||||
|
||||
## Documentation
|
||||
|
||||
Comprehensive developer and user documentation can be found in our:
|
||||
|
||||
### 📚 [Speckle Docs website](https://speckle.guide/dev/)
|
||||
|
||||
# Developing and Debugging
|
||||
|
||||
### Building
|
||||
|
||||
Ensure you're using a [8.0.4xx](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) .NET SDK.
|
||||
After cloning this repository, just restore all the NuGet packages and hit Build!
|
||||
|
||||
### Developing
|
||||
|
||||
It is highly recommended you use
|
||||
- Either Jetbrains Rider or Visual Studio 2022
|
||||
- Ensure your IDE is set to use [the correct .NET SDK version](https://github.com/specklesystems/speckle-sharp-sdk/blob/main/global.json) (newer major versions may work, but may incorrectly run analysers we haven't configured)
|
||||
- You should install the cshapier plugin ([Rider](https://plugins.jetbrains.com/plugin/18243-csharpier), [VS](https://marketplace.visualstudio.com/items?itemName=csharpier.CSharpier)) and configure it to run on save
|
||||
|
||||
Docs are a bit patchy [https://docs.speckle.systems/developers/looking-for-developer-docs](https://docs.speckle.systems/developers/looking-for-developer-docs)
|
||||
|
||||
### Tests
|
||||
|
||||
There are several test projects. It is a requirement that all tests pass for PRs to be merged.
|
||||
The Integration test projects require a local server to be running.
|
||||
|
||||
You must have docker installed. Then you can run `docker compose up --wait` from the root of the repo to start the required containers.
|
||||
|
||||
## Contributing
|
||||
|
||||
Before embarking on submitting a patch, please make sure you read:
|
||||
|
||||
- [Contribution Guidelines](CONTRIBUTING.md)
|
||||
- [Code of Conduct](CODE_OF_CONDUCT.md)
|
||||
|
||||
# Security and Licensing
|
||||
|
||||
### Security
|
||||
|
||||
For any security vulnerabilities or concerns, please contact us directly at security[at]speckle.systems.
|
||||
|
||||
### License
|
||||
|
||||
Unless otherwise described, the code in this repository is licensed under the Apache-2.0 License. Please note that some modules, extensions or code herein might be otherwise licensed. This is indicated either in the root of the containing folder under a different license file, or in the respective file's header. If you have any questions, don't hesitate to get in touch with us via [email](mailto:hello@speckle.systems).
|
||||
|
||||
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk", "src\Speckle.Sdk\Speckle.Sdk.csproj", "{A413E196-3696-4F48-B635-04B5F76BF9C9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk.Tests.Unit", "tests\Speckle.Sdk.Tests.Unit\Speckle.Sdk.Tests.Unit.csproj", "{99AE2273-12C5-4A9D-9FDD-19F8B394B5E2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Objects", "src\Speckle.Objects\Speckle.Objects.csproj", "{181F50AA-DD2A-4541-98EF-B868E2D06B9A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Objects.Tests.Unit", "tests\Speckle.Objects.Tests.Unit\Speckle.Objects.Tests.Unit.csproj", "{A0338FC0-3011-498F-AD09-01230FABD3ED}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CB96C27-FC5B-4A41-86B6-951AF99B8116}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\graphql.config.yml = src\graphql.config.yml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{35047EE7-AD1D-4741-80A7-8F0E874718E9}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{DA2AED52-58F9-471E-8AD8-102FD36129E3}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.csharpierrc.yaml = .csharpierrc.yaml
|
||||
.editorconfig = .editorconfig
|
||||
Directory.Build.props = Directory.Build.props
|
||||
Directory.Packages.props = Directory.Packages.props
|
||||
global.json = global.json
|
||||
README.md = README.md
|
||||
docker-compose.yml = docker-compose.yml
|
||||
CodeMetricsConfig.txt = CodeMetricsConfig.txt
|
||||
Directory.Build.Targets = Directory.Build.Targets
|
||||
.config\dotnet-tools.json = .config\dotnet-tools.json
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{58D37DA9-F948-48CA-9A73-F5BBBD533DBF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "build", "build\build.csproj", "{9B8DDEB5-37C7-49B5-984D-C65DE5FCB7B7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk.Serialization.Tests", "tests\Speckle.Sdk.Serialization.Tests\Speckle.Sdk.Serialization.Tests.csproj", "{AA1E1E51-49AE-4F71-84B1-938E19695BE0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk.Tests.Integration", "tests\Speckle.Sdk.Tests.Integration\Speckle.Sdk.Tests.Integration.csproj", "{4FB41A6D-D139-4111-8115-E3F9F6BEAF24}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{B623BD21-5CAA-43F9-A539-1835276C220E}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.github\workflows\pr.yml = .github\workflows\pr.yml
|
||||
.github\workflows\release.yml = .github\workflows\release.yml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk.Tests.Performance", "tests\Speckle.Sdk.Tests.Performance\Speckle.Sdk.Tests.Performance.csproj", "{870E3396-E6F7-43AE-B120-E651FA4F46BD}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk.Serialization.Testing", "tests\Speckle.Sdk.Serialization.Testing\Speckle.Sdk.Serialization.Testing.csproj", "{FF922B6D-D416-4348-8CB8-0C8B28691070}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk.Dependencies", "src\Speckle.Sdk.Dependencies\Speckle.Sdk.Dependencies.csproj", "{27584AB4-8ACD-4850-8CC2-7E5BC739FB78}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk.Testing", "tests\Speckle.Sdk.Testing\Speckle.Sdk.Testing.csproj", "{7B617C0D-2354-415C-993C-5071D4113E27}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "performance", "performance", "{FFB07238-87E8-463A-AA39-3B38AAAA94C1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Automate.Sdk", "src\Speckle.Automate.Sdk\Speckle.Automate.Sdk.csproj", "{4EB20EFA-5A38-415E-B3FD-29CA3ACD1EF5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Automate.Sdk.Integration", "tests\Speckle.Automate.Sdk.Integration\Speckle.Automate.Sdk.Integration.csproj", "{B6129DC3-F285-4E5F-85E2-6D2533A4005E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{8781B61F-0308-488A-BEB2-1939E7CEEBE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8781B61F-0308-488A-BEB2-1939E7CEEBE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8781B61F-0308-488A-BEB2-1939E7CEEBE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8781B61F-0308-488A-BEB2-1939E7CEEBE9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A413E196-3696-4F48-B635-04B5F76BF9C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A413E196-3696-4F48-B635-04B5F76BF9C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A413E196-3696-4F48-B635-04B5F76BF9C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A413E196-3696-4F48-B635-04B5F76BF9C9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{99AE2273-12C5-4A9D-9FDD-19F8B394B5E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{99AE2273-12C5-4A9D-9FDD-19F8B394B5E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{99AE2273-12C5-4A9D-9FDD-19F8B394B5E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{99AE2273-12C5-4A9D-9FDD-19F8B394B5E2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{181F50AA-DD2A-4541-98EF-B868E2D06B9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{181F50AA-DD2A-4541-98EF-B868E2D06B9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{181F50AA-DD2A-4541-98EF-B868E2D06B9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{181F50AA-DD2A-4541-98EF-B868E2D06B9A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A0338FC0-3011-498F-AD09-01230FABD3ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A0338FC0-3011-498F-AD09-01230FABD3ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A0338FC0-3011-498F-AD09-01230FABD3ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A0338FC0-3011-498F-AD09-01230FABD3ED}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9B8DDEB5-37C7-49B5-984D-C65DE5FCB7B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9B8DDEB5-37C7-49B5-984D-C65DE5FCB7B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9B8DDEB5-37C7-49B5-984D-C65DE5FCB7B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9B8DDEB5-37C7-49B5-984D-C65DE5FCB7B7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AA1E1E51-49AE-4F71-84B1-938E19695BE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AA1E1E51-49AE-4F71-84B1-938E19695BE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AA1E1E51-49AE-4F71-84B1-938E19695BE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AA1E1E51-49AE-4F71-84B1-938E19695BE0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4FB41A6D-D139-4111-8115-E3F9F6BEAF24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4FB41A6D-D139-4111-8115-E3F9F6BEAF24}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4FB41A6D-D139-4111-8115-E3F9F6BEAF24}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4FB41A6D-D139-4111-8115-E3F9F6BEAF24}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{870E3396-E6F7-43AE-B120-E651FA4F46BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{870E3396-E6F7-43AE-B120-E651FA4F46BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{870E3396-E6F7-43AE-B120-E651FA4F46BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{870E3396-E6F7-43AE-B120-E651FA4F46BD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FF922B6D-D416-4348-8CB8-0C8B28691070}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FF922B6D-D416-4348-8CB8-0C8B28691070}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FF922B6D-D416-4348-8CB8-0C8B28691070}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FF922B6D-D416-4348-8CB8-0C8B28691070}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{27584AB4-8ACD-4850-8CC2-7E5BC739FB78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{27584AB4-8ACD-4850-8CC2-7E5BC739FB78}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{27584AB4-8ACD-4850-8CC2-7E5BC739FB78}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{27584AB4-8ACD-4850-8CC2-7E5BC739FB78}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7B617C0D-2354-415C-993C-5071D4113E27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7B617C0D-2354-415C-993C-5071D4113E27}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7B617C0D-2354-415C-993C-5071D4113E27}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7B617C0D-2354-415C-993C-5071D4113E27}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4EB20EFA-5A38-415E-B3FD-29CA3ACD1EF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4EB20EFA-5A38-415E-B3FD-29CA3ACD1EF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4EB20EFA-5A38-415E-B3FD-29CA3ACD1EF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4EB20EFA-5A38-415E-B3FD-29CA3ACD1EF5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B6129DC3-F285-4E5F-85E2-6D2533A4005E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B6129DC3-F285-4E5F-85E2-6D2533A4005E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B6129DC3-F285-4E5F-85E2-6D2533A4005E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B6129DC3-F285-4E5F-85E2-6D2533A4005E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{A413E196-3696-4F48-B635-04B5F76BF9C9} = {5CB96C27-FC5B-4A41-86B6-951AF99B8116}
|
||||
{181F50AA-DD2A-4541-98EF-B868E2D06B9A} = {5CB96C27-FC5B-4A41-86B6-951AF99B8116}
|
||||
{99AE2273-12C5-4A9D-9FDD-19F8B394B5E2} = {35047EE7-AD1D-4741-80A7-8F0E874718E9}
|
||||
{A0338FC0-3011-498F-AD09-01230FABD3ED} = {35047EE7-AD1D-4741-80A7-8F0E874718E9}
|
||||
{9B8DDEB5-37C7-49B5-984D-C65DE5FCB7B7} = {58D37DA9-F948-48CA-9A73-F5BBBD533DBF}
|
||||
{AA1E1E51-49AE-4F71-84B1-938E19695BE0} = {35047EE7-AD1D-4741-80A7-8F0E874718E9}
|
||||
{4FB41A6D-D139-4111-8115-E3F9F6BEAF24} = {35047EE7-AD1D-4741-80A7-8F0E874718E9}
|
||||
{B623BD21-5CAA-43F9-A539-1835276C220E} = {DA2AED52-58F9-471E-8AD8-102FD36129E3}
|
||||
{27584AB4-8ACD-4850-8CC2-7E5BC739FB78} = {5CB96C27-FC5B-4A41-86B6-951AF99B8116}
|
||||
{7B617C0D-2354-415C-993C-5071D4113E27} = {35047EE7-AD1D-4741-80A7-8F0E874718E9}
|
||||
{FF922B6D-D416-4348-8CB8-0C8B28691070} = {FFB07238-87E8-463A-AA39-3B38AAAA94C1}
|
||||
{870E3396-E6F7-43AE-B120-E651FA4F46BD} = {FFB07238-87E8-463A-AA39-3B38AAAA94C1}
|
||||
{4EB20EFA-5A38-415E-B3FD-29CA3ACD1EF5} = {5CB96C27-FC5B-4A41-86B6-951AF99B8116}
|
||||
{B6129DC3-F285-4E5F-85E2-6D2533A4005E} = {35047EE7-AD1D-4741-80A7-8F0E874718E9}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,3 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=QL/@EntryIndexedValue">QL</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XYZ/@EntryIndexedValue">XYZ</s:String></wpf:ResourceDictionary>
|
||||
@@ -0,0 +1,45 @@
|
||||
<Solution>
|
||||
<Folder Name="/build/">
|
||||
<Project Path="build/build.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/config/">
|
||||
<File Path=".config/dotnet-tools.json" />
|
||||
<File Path=".csharpierrc.yaml" />
|
||||
<File Path=".editorconfig" />
|
||||
<File Path="CodeMetricsConfig.txt" />
|
||||
<File Path="Directory.Build.props" />
|
||||
<File Path="Directory.Build.Targets" />
|
||||
<File Path="Directory.Packages.props" />
|
||||
<File Path="docker-compose.yml" />
|
||||
<File Path="global.json" />
|
||||
<File Path="README.md" />
|
||||
<File Path=".github\copilot-instructions.md" />
|
||||
<File Path=".github\git-commit-instructions.md" />
|
||||
</Folder>
|
||||
<Folder Name="/config/workflows/">
|
||||
<File Path=".github/workflows/pr.yml" />
|
||||
<File Path=".github/workflows/release.yml" />
|
||||
</Folder>
|
||||
<Folder Name="/performance/">
|
||||
<Project Path="tests/Speckle.Sdk.Serialization.Testing/Speckle.Sdk.Serialization.Testing.csproj" />
|
||||
<Project Path="tests/Speckle.Sdk.Tests.Performance/Speckle.Sdk.Tests.Performance.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/src/">
|
||||
<Project Path="src/Speckle.Automate.Sdk/Speckle.Automate.Sdk.csproj" />
|
||||
<Project Path="src/Speckle.Objects/Speckle.Objects.csproj" />
|
||||
<Project Path="src/Speckle.Sdk.Dependencies/Speckle.Sdk.Dependencies.csproj" />
|
||||
<Project Path="src/Speckle.Sdk/Speckle.Sdk.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/tests/">
|
||||
<Project Path="tests/Speckle.Sdk.Testing/Speckle.Sdk.Testing.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/tests/integration/">
|
||||
<Project Path="tests/Speckle.Automate.Sdk.Integration/Speckle.Automate.Sdk.Integration.csproj" />
|
||||
<Project Path="tests/Speckle.Sdk.Tests.Integration/Speckle.Sdk.Tests.Integration.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/tests/unit/">
|
||||
<Project Path="tests/Speckle.Objects.Tests.Unit/Speckle.Objects.Tests.Unit.csproj" />
|
||||
<Project Path="tests/Speckle.Sdk.Serialization.Tests/Speckle.Sdk.Serialization.Tests.csproj" />
|
||||
<Project Path="tests/Speckle.Sdk.Tests.Unit/Speckle.Sdk.Tests.Unit.csproj" />
|
||||
</Folder>
|
||||
</Solution>
|
||||
@@ -0,0 +1,3 @@
|
||||
$ErrorActionPreference = "Stop";
|
||||
|
||||
dotnet run --project build/build.csproj -- $args
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
dotnet run --project build/build.csproj -- "$@"
|
||||
@@ -0,0 +1,185 @@
|
||||
using GlobExpressions;
|
||||
using static Bullseye.Targets;
|
||||
using static SimpleExec.Command;
|
||||
|
||||
const string CLEAN = "clean";
|
||||
const string FORMAT = "format";
|
||||
const string RESTORE_TOOLS = "restore-tools";
|
||||
|
||||
const string RESTORE = "restore";
|
||||
const string BUILD = "build";
|
||||
const string TEST = "test";
|
||||
const string INTEGRATION = "integration";
|
||||
const string PACK = "pack";
|
||||
const string CLEAN_LOCKS = "clean-locks";
|
||||
const string PERF = "perf";
|
||||
const string DEEP_CLEAN = "deep-clean";
|
||||
|
||||
static (string semver, string fileVerison) GetVersions()
|
||||
{
|
||||
string semver =
|
||||
Environment.GetEnvironmentVariable("SEMVER") ?? throw new ArgumentException("Expected SEMVER env var");
|
||||
string fileVersion =
|
||||
Environment.GetEnvironmentVariable("FILE_VERSION") ?? throw new ArgumentException("Expected FILE_VERSION env var");
|
||||
return (semver, fileVersion);
|
||||
}
|
||||
|
||||
Target(
|
||||
CLEAN_LOCKS,
|
||||
() =>
|
||||
{
|
||||
foreach (var f in Glob.Files(".", "**/*.lock.json"))
|
||||
{
|
||||
Console.WriteLine("Found and will delete: " + f);
|
||||
File.Delete(f);
|
||||
}
|
||||
Console.WriteLine("Running restore now.");
|
||||
Run("dotnet", "restore .\\Speckle.Sdk.sln");
|
||||
}
|
||||
);
|
||||
|
||||
Target(
|
||||
CLEAN,
|
||||
forEach: ["**/output"],
|
||||
dir =>
|
||||
{
|
||||
IEnumerable<string> GetDirectories(string d)
|
||||
{
|
||||
return Glob.Directories(".", d);
|
||||
}
|
||||
|
||||
void RemoveDirectory(string d)
|
||||
{
|
||||
if (Directory.Exists(d))
|
||||
{
|
||||
Console.WriteLine(d);
|
||||
Directory.Delete(d, true);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var d in GetDirectories(dir))
|
||||
{
|
||||
RemoveDirectory(d);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Target(RESTORE_TOOLS, () => RunAsync("dotnet", "tool restore"));
|
||||
|
||||
Target(FORMAT, dependsOn: [RESTORE_TOOLS], () => RunAsync("dotnet", "csharpier check ."));
|
||||
|
||||
Target(RESTORE, dependsOn: [FORMAT], () => RunAsync("dotnet", "restore Speckle.Sdk.sln --locked-mode"));
|
||||
|
||||
Target(
|
||||
BUILD,
|
||||
dependsOn: [RESTORE],
|
||||
async () =>
|
||||
{
|
||||
var (version, fileVersion) = GetVersions();
|
||||
Console.WriteLine($"Version: {version} & {fileVersion}");
|
||||
await RunAsync(
|
||||
"dotnet",
|
||||
$"build Speckle.Sdk.sln -c Release --no-restore -warnaserror -p:Version={version} -p:FileVersion={fileVersion}"
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
);
|
||||
|
||||
Target(
|
||||
TEST,
|
||||
dependsOn: [BUILD],
|
||||
Glob.Files(".", "**/*.Tests.Unit.csproj").Concat(Glob.Files(".", "**/*.Tests.csproj")),
|
||||
async file =>
|
||||
{
|
||||
await RunAsync(
|
||||
"dotnet",
|
||||
$"test {file} -c Release --no-build --no-restore --verbosity=normal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage /p:AltCoverVerbosity=Warning"
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
);
|
||||
|
||||
Target(
|
||||
INTEGRATION,
|
||||
dependsOn: [BUILD],
|
||||
async () =>
|
||||
{
|
||||
await RunAsync("docker", "compose -f docker-compose.yml up --wait").ConfigureAwait(false);
|
||||
foreach (var test in Glob.Files(".", "**/*.Tests.Integration.csproj"))
|
||||
{
|
||||
await RunAsync(
|
||||
"dotnet",
|
||||
$"test {test} -c Release --no-build --no-restore --verbosity=normal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage"
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
await RunAsync("docker", "compose down").ConfigureAwait(false);
|
||||
}
|
||||
);
|
||||
|
||||
Target(
|
||||
PERF,
|
||||
Glob.Files(".", "**/*.Tests.Performance.csproj"),
|
||||
async file =>
|
||||
{
|
||||
void CheckBuildDirectory(string dir, string build)
|
||||
{
|
||||
var binDir = Path.Combine(dir, "bin", build);
|
||||
Console.WriteLine($"Checking: {binDir}");
|
||||
if (Directory.Exists(binDir))
|
||||
{
|
||||
Directory.Delete(binDir, true);
|
||||
Console.WriteLine($"Deleted: {binDir}");
|
||||
}
|
||||
}
|
||||
var dir = Path.GetDirectoryName(file) ?? throw new InvalidOperationException();
|
||||
CheckBuildDirectory(dir, "Release");
|
||||
CheckBuildDirectory(dir, "Debug");
|
||||
await RunAsync("dotnet", $"run --project {file} -c Release").ConfigureAwait(false);
|
||||
}
|
||||
);
|
||||
|
||||
Target(
|
||||
DEEP_CLEAN,
|
||||
() =>
|
||||
{
|
||||
foreach (var f in Glob.Directories(".", "**/bin"))
|
||||
{
|
||||
if (f.StartsWith("build"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Console.WriteLine("Found and will delete: " + f);
|
||||
Directory.Delete(f, true);
|
||||
}
|
||||
foreach (var f in Glob.Directories(".", "**/obj"))
|
||||
{
|
||||
if (f.StartsWith("Build"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Console.WriteLine("Found and will delete: " + f);
|
||||
Directory.Delete(f, true);
|
||||
}
|
||||
Console.WriteLine("Running restore now.");
|
||||
Run("dotnet", "restore .\\Speckle.Sdk.sln --no-cache");
|
||||
}
|
||||
);
|
||||
|
||||
Target(
|
||||
PACK,
|
||||
dependsOn: [TEST],
|
||||
async () =>
|
||||
{
|
||||
{
|
||||
var (version, fileVersion) = GetVersions();
|
||||
Console.WriteLine($"Version: {version} & {fileVersion}");
|
||||
await RunAsync("dotnet", $"pack Speckle.Sdk.sln -c Release -o output --no-build -p:Version={version}")
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Target("default", dependsOn: [FORMAT, TEST, INTEGRATION], () => Console.WriteLine("Done!"));
|
||||
|
||||
await RunTargetsAndExitAsync(args).ConfigureAwait(true);
|
||||
@@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Bullseye" />
|
||||
<PackageReference Include="Glob" />
|
||||
<PackageReference Include="SimpleExec" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dependencies": {
|
||||
"net8.0": {
|
||||
"Bullseye": {
|
||||
"type": "Direct",
|
||||
"requested": "[6.0.0, )",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "vgwwXfzs7jJrskWH7saHRMgPzziq/e86QZNWY1MnMxd7e+De7E7EX4K3C7yrvaK9y02SJoLxNxcLG/q5qUAghw=="
|
||||
},
|
||||
"Glob": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.1.9, )",
|
||||
"resolved": "1.1.9",
|
||||
"contentHash": "AfK5+ECWYTP7G3AAdnU8IfVj+QpGjrh9GC2mpdcJzCvtQ4pnerAGwHsxJ9D4/RnhDUz2DSzd951O/lQjQby2Sw=="
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Build.Tasks.Git": "8.0.0",
|
||||
"Microsoft.SourceLink.Common": "8.0.0"
|
||||
}
|
||||
},
|
||||
"PolySharp": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.15.0, )",
|
||||
"resolved": "1.15.0",
|
||||
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
|
||||
},
|
||||
"SimpleExec": {
|
||||
"type": "Direct",
|
||||
"requested": "[12.0.0, )",
|
||||
"resolved": "12.0.0",
|
||||
"contentHash": "ptxlWtxC8vM6Y6e3h9ZTxBBkOWnWrm/Sa1HT+2i1xcXY3Hx2hmKDZP5RShPf8Xr9D+ivlrXNy57ktzyH8kyt+Q=="
|
||||
},
|
||||
"Speckle.InterfaceGenerator": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.9.6, )",
|
||||
"resolved": "0.9.6",
|
||||
"contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w=="
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.SourceLink.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
informational: true
|
||||
target: auto
|
||||
threshold: 1%
|
||||
base: auto
|
||||
patch:
|
||||
default:
|
||||
informational: true
|
||||
github_checks:
|
||||
annotations: false
|
||||
@@ -0,0 +1,120 @@
|
||||
name: "speckle-server"
|
||||
|
||||
services:
|
||||
####
|
||||
# Speckle Server dependencies
|
||||
#######
|
||||
postgres:
|
||||
image: "postgres:16.4-alpine3.20@sha256:d898b0b78a2627cb4ee63464a14efc9d296884f1b28c841b0ab7d7c42f1fffdf"
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_DB: speckle
|
||||
POSTGRES_USER: speckle
|
||||
POSTGRES_PASSWORD: speckle
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data/
|
||||
healthcheck:
|
||||
# the -U user has to match the POSTGRES_USER value
|
||||
test: ["CMD-SHELL", "pg_isready -U speckle"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
|
||||
redis:
|
||||
image: "valkey/valkey:8.1-alpine@sha256:0d27f0bca0249f61d060029a6aaf2e16b2c417d68d02a508e1dfb763fa2948b4"
|
||||
restart: always
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
|
||||
minio:
|
||||
image: "minio/minio:RELEASE.2023-10-25T06-33-25Z"
|
||||
command: server /data --console-address ":9001"
|
||||
restart: always
|
||||
volumes:
|
||||
- minio-data:/data
|
||||
ports:
|
||||
- '127.0.0.1:9000:9000'
|
||||
- '127.0.0.1:9001:9001'
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
"curl -s -o /dev/null http://127.0.0.1:9000/minio/index.html",
|
||||
]
|
||||
interval: 5s
|
||||
timeout: 30s
|
||||
retries: 30
|
||||
start_period: 10s
|
||||
|
||||
speckle-server:
|
||||
image: speckle/speckle-server:latest
|
||||
restart: always
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- /nodejs/bin/node
|
||||
- -e
|
||||
- "try { require('node:http').request({headers: {'Content-Type': 'application/json'}, port:3000, hostname:'127.0.0.1', path:'/readiness', method: 'GET', timeout: 2000 }, (res) => { body = ''; res.on('data', (chunk) => {body += chunk;}); res.on('end', () => {process.exit(Number(res.statusCode != 200 || body.toLowerCase().includes('error')));}); }).end(); } catch { process.exit(1); }"
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 90s
|
||||
ports:
|
||||
- "0.0.0.0:3000:3000"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
minio:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
# TODO: Change this to the URL of the speckle server, as accessed from the network
|
||||
CANONICAL_URL: "http://127.0.0.1:8080"
|
||||
SPECKLE_AUTOMATE_URL: "http://127.0.0.1:3030"
|
||||
FRONTEND_ORIGIN: "http://127.0.0.1:8081"
|
||||
|
||||
# TODO: Change thvolumes:
|
||||
REDIS_URL: "redis://redis"
|
||||
|
||||
S3_ENDPOINT: "http://minio:9000"
|
||||
S3_PUBLIC_ENDPOINT: 'http://127.0.0.1:9000'
|
||||
S3_ACCESS_KEY: "minioadmin"
|
||||
S3_SECRET_KEY: "minioadmin"
|
||||
S3_BUCKET: "speckle-server"
|
||||
S3_CREATE_BUCKET: "true"
|
||||
|
||||
FILE_SIZE_LIMIT_MB: 100
|
||||
MAX_PROJECT_MODELS_PER_PAGE: 500
|
||||
|
||||
# TODO: Change this to a unique secret for this server
|
||||
SESSION_SECRET: "TODO:ReplaceWithLongString"
|
||||
|
||||
STRATEGY_LOCAL: "true"
|
||||
DEBUG: "speckle:*"
|
||||
|
||||
POSTGRES_URL: "postgres"
|
||||
POSTGRES_USER: "speckle"
|
||||
POSTGRES_PASSWORD: "speckle"
|
||||
POSTGRES_DB: "speckle"
|
||||
ENABLE_MP: "false"
|
||||
|
||||
LOG_PRETTY: "true"
|
||||
|
||||
FF_NEXT_GEN_FILE_IMPORTER_ENABLED: "true"
|
||||
FF_LARGE_FILE_IMPORTS_ENABLED: "true"
|
||||
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: speckle-server
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
redis-data:
|
||||
minio-data:
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "8.0.400",
|
||||
"rollForward": "latestMinor"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
|
||||
|
||||
## Storage Size
|
||||
1 million objects => 540mb ( based on ~= 4.2 million objects => 2.3GB not gzipped)
|
||||
|
||||
1 million objects => 127mb gzipped
|
||||
|
||||
4x reduction in space
|
||||
|
||||
## Local storage takeaways:
|
||||
|
||||
SQLite optimisations make a difference in insertion speed. Insertion speed does slow down on large tables (+1m rows).
|
||||
|
||||
Partioned tables (by, for example, the first two decimals of the hash) have slower but predictable insertion speed. Not sure if compromise is worth it?
|
||||
|
||||
## Even More Optimised single object table
|
||||
Optimisations are:
|
||||
- `PRAGMA journal_mode = MEMORY;`
|
||||
- `PRAGMA synchronous = OFF;`
|
||||
- `PRAGMA count_changes=OFF;`
|
||||
- `PRAGMA temp_store=MEMORY;`
|
||||
|
||||
|
||||
### Test 1
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 2286 ms -> 50000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 1426 ms -> 100000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
### Test 2
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 3052 ms -> 33333.333333333336 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 2244 ms -> 50000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
### Test 3
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 4941 ms -> 25000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 2555 ms -> 50000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
### Test 4
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 8022 ms -> 12500 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 3350 ms -> 33333.333333333336 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
|
||||
### Test 5
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 6602 ms -> 16666.666666666668 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 3445 ms -> 33333.333333333336 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
### Test 5+: A couple of more rounds, pushing objs to 2.000k
|
||||
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 7332 ms -> 14285.714285714286 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 7625 ms -> 14285.714285714286 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 7539 ms -> 14285.714285714286 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 4249 ms -> 25000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 8300 ms -> 12500 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 7289 ms -> 14285.714285714286 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 8668 ms -> 12500 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 8060 ms -> 12500 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
|
||||
|
||||
Starting to save 100000 of objects
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 10228 ms -> 10000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 11475 ms -> 9090.90909090909 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
Starting to save 100000 of objects
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 12540 ms -> 8333.333333333334 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 7113 ms -> 14285.714285714286 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
Starting to save 100000 of objects
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 17153 ms -> 5882.35294117647 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 5997 ms -> 20000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
Starting to save 100000 of objects
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 20841 ms -> 5000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 9195 ms -> 11111.111111111111 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
Starting to save 100000 of objects
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 13404 ms -> 7692.307692307692 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 7529 ms -> 14285.714285714286 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
Starting to save 100000 of objects
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 19806 ms -> 5263.1578947368425 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 7318 ms -> 14285.714285714286 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
Starting to save 100000 of objects
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 24612 ms -> 4166.666666666667 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 7410 ms -> 14285.714285714286 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
Starting to save 100000 of objects
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 22257 ms -> 4545.454545454545 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 18699 ms -> 5555.555555555556 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
Starting to save 100000 of objects
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 20947 ms -> 5000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 14089 ms -> 7142.857142857143 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
|
||||
## Optimised single object table
|
||||
Optimisations are: `PRAGMA journal_mode = MEMORY;` and `PRAGMA synchronous = OFF;`
|
||||
|
||||
### Test 1:
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 2267 ms -> 50000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 1327 ms -> 100000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
### Test 2:
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 4532 ms -> 25000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 2243 ms -> 50000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
### Test 3:
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 3768 ms -> 33333.333333333336 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 5295 ms -> 20000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
### Test 4:
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 4033 ms -> 25000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 3126 ms -> 33333.333333333336 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
### Test 5:
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 4432 ms -> 25000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 3527 ms -> 33333.333333333336 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
|
||||
## Single object table
|
||||
|
||||
### Test 1:
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 11964 ms -> 9090.90909090909 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 6875 ms -> 16666.666666666668 objects per second
|
||||
-------------------------------------------------
|
||||
200k total in db
|
||||
|
||||
### Test 2:
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 21956 ms -> 4761.9047619047615 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 8904 ms -> 12500 objects per second
|
||||
-------------------------------------------------
|
||||
400k total in db
|
||||
|
||||
### Test 3:
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 25532 ms -> 4000 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 10124 ms -> 10000 objects per second
|
||||
-------------------------------------------------
|
||||
600k total in db
|
||||
|
||||
### Test 4:
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 26629 ms -> 3846.153846153846 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 10610 ms -> 10000 objects per second
|
||||
-------------------------------------------------
|
||||
800k total in db
|
||||
|
||||
### Test 5:
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 26956 ms -> 3846.153846153846 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 11007 ms -> 9090.90909090909 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
1000k total in db
|
||||
|
||||
|
||||
## Bucketed Object Table (256 individual tables for objects):
|
||||
Pre-generate 256 tables, of form `objs${prefix}`, where prefix is the cartesian product of all the valid hex decimals (`0-9, a-f`).
|
||||
|
||||
### Test 1:
|
||||
Forgot to copy paste.
|
||||
200k total in db
|
||||
|
||||
### Test 2:
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 19096 ms -> 5263.1578947368425 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 7401 ms -> 14285.714285714286 objects per second
|
||||
-------------------------------------------------
|
||||
400k total in db
|
||||
|
||||
### Test 3:
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 22477 ms -> 4545.454545454545 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 8668 ms -> 12500 objects per second
|
||||
-------------------------------------------------
|
||||
600k total in db
|
||||
|
||||
### Test 4:
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 23438 ms -> 4347.826086956522 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 9288 ms -> 11111.111111111111 objects per second
|
||||
-------------------------------------------------
|
||||
800k total in db
|
||||
|
||||
### Test 5:
|
||||
-------------------------------------------------
|
||||
BufferedWriteTest: Wrote 100000 in 23735 ms -> 4347.826086956522 objects per second
|
||||
-------------------------------------------------
|
||||
|
||||
-------------------------------------------------
|
||||
BulkWriteMany: Wrote 100000 in 9944 ms -> 11111.111111111111 objects per second
|
||||
-------------------------------------------------
|
||||
1mil total in db
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using GraphQL;
|
||||
using GraphQL.Client.Http;
|
||||
using Speckle.Automate.Sdk.Schema;
|
||||
using Speckle.InterfaceGenerator;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Credentials;
|
||||
|
||||
namespace Speckle.Automate.Sdk;
|
||||
|
||||
[GenerateAutoInterface(VisibilityModifier = "public")]
|
||||
internal sealed class AutomationContextFactory(
|
||||
IClientFactory clientFactory,
|
||||
IAccountFactory accountFactory,
|
||||
IOperations operations
|
||||
) : IAutomationContextFactory
|
||||
{
|
||||
private static readonly JsonSerializerOptions s_jsonSerializerSettings = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
};
|
||||
|
||||
/// <inheritdoc cref="Initialize(AutomationRunData, string)"/>
|
||||
public async Task<IAutomationContext> Initialize(string automationRunData, string speckleToken)
|
||||
{
|
||||
var runData = JsonSerializer.Deserialize<AutomationRunData>(automationRunData, s_jsonSerializerSettings);
|
||||
|
||||
return await Initialize(runData, speckleToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Initialize(AutomationRunData, Account)"/>
|
||||
/// <exception cref="GraphQLHttpRequestException">Request failed on the HTTP layer (received a non-successful response code)</exception>
|
||||
/// <exception cref="AggregateException"><inheritdoc cref="Speckle.Sdk.Api.GraphQL.GraphQLErrorHandler.EnsureGraphQLSuccess(IGraphQLResponse)"/></exception>
|
||||
public async Task<IAutomationContext> Initialize(AutomationRunData automationRunData, string speckleToken)
|
||||
{
|
||||
Account account = await accountFactory
|
||||
.CreateAccount(automationRunData.SpeckleServerUrl, speckleToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return Initialize(automationRunData, account);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="AutomationContext"/> from the provided data
|
||||
/// </summary>
|
||||
public IAutomationContext Initialize(AutomationRunData automationRunData, Account account)
|
||||
{
|
||||
IClient client = clientFactory.Create(account);
|
||||
Stopwatch initTime = Stopwatch.StartNew();
|
||||
|
||||
return new AutomationContext(operations)
|
||||
{
|
||||
AutomationRunData = automationRunData,
|
||||
SpeckleClient = client,
|
||||
_speckleToken = account.token,
|
||||
_initTime = initTime,
|
||||
AutomationResult = new AutomationResult(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using GraphQL;
|
||||
using Speckle.Automate.Sdk.Schema;
|
||||
using Speckle.Automate.Sdk.Schema.Triggers;
|
||||
using Speckle.InterfaceGenerator;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Inputs;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
|
||||
|
||||
namespace Speckle.Automate.Sdk;
|
||||
|
||||
[GenerateAutoInterface(VisibilityModifier = "public")]
|
||||
internal sealed class AutomationContext(IOperations operations) : IAutomationContext
|
||||
{
|
||||
public AutomationRunData AutomationRunData { get; set; }
|
||||
public string? ContextView
|
||||
{
|
||||
get => AutomationResult.ResultView;
|
||||
private set => AutomationResult.ResultView = value;
|
||||
}
|
||||
public required IClient SpeckleClient { get; init; }
|
||||
|
||||
public required string _speckleToken { get; init; }
|
||||
|
||||
// added for performance measuring
|
||||
public required Stopwatch _initTime { get; init; }
|
||||
|
||||
public required AutomationResult AutomationResult { get; init; }
|
||||
|
||||
public string RunStatus => AutomationResult.RunStatus;
|
||||
|
||||
public string? StatusMessage => AutomationResult.StatusMessage;
|
||||
public TimeSpan Elapsed => _initTime.Elapsed;
|
||||
|
||||
/// <summary>
|
||||
/// Receive version for automation.
|
||||
/// </summary>
|
||||
/// <returns> Commit object. </returns>
|
||||
/// <exception cref="SpeckleException">Throws if commit object is null.</exception>
|
||||
public async Task<Base> ReceiveVersion(CancellationToken cancellationToken = default)
|
||||
{
|
||||
// TODO: this is a quick hack to keep implementation consistency. Move to proper receive many versions
|
||||
if (AutomationRunData.Triggers.First() is not VersionCreationTrigger trigger)
|
||||
{
|
||||
throw new SpeckleException("Processed automation run data without any triggers");
|
||||
}
|
||||
var versionId = trigger.Payload.VersionId;
|
||||
|
||||
var version = await SpeckleClient
|
||||
.Version.Get(versionId, AutomationRunData.ProjectId, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (version.referencedObject == null)
|
||||
{
|
||||
throw new SpeckleException(
|
||||
"The requested speckle model version has exceeded workspace version history limits or the reference object is otherwise null"
|
||||
);
|
||||
}
|
||||
|
||||
Base? rootObject = await operations
|
||||
.Receive2(
|
||||
SpeckleClient.ServerUrl,
|
||||
AutomationRunData.ProjectId,
|
||||
version.referencedObject,
|
||||
SpeckleClient.Account.token,
|
||||
null,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
Console.WriteLine($"It took {Elapsed.TotalSeconds} seconds to receive the speckle version {versionId}");
|
||||
return rootObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new version in the project.
|
||||
/// </summary>
|
||||
/// <param name="rootObject">Object to send to project.</param>
|
||||
/// <param name="model">The model to create the version under</param>
|
||||
/// <param name="versionMessage">Version message.</param>
|
||||
/// <param name="cancellationToken">Version message.</param>
|
||||
/// <returns>Version id.</returns>
|
||||
/// <exception cref="SpeckleException"> Throws if given model name is as same as with model name in automation run data.
|
||||
/// The reason is to prevent circular run loop in automation.</exception>
|
||||
public async Task<Version> CreateNewVersionInProject(
|
||||
Base rootObject,
|
||||
Model model,
|
||||
string versionMessage = "",
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
// Confirm target branch is not the same as source branch
|
||||
foreach (var trigger in AutomationRunData.Triggers)
|
||||
{
|
||||
if (trigger.Payload.ModelId == model.id)
|
||||
{
|
||||
throw new SpeckleException(
|
||||
$"""
|
||||
The target model: {model.name} ({model.id}) cannot match the model
|
||||
that triggered this automation:
|
||||
{trigger.Payload.ModelId}
|
||||
"""
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var (rootObjectId, _) = await operations
|
||||
.Send2(
|
||||
SpeckleClient.ServerUrl,
|
||||
AutomationRunData.ProjectId,
|
||||
SpeckleClient.Account.token,
|
||||
rootObject,
|
||||
null,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var newVersion = await SpeckleClient
|
||||
.Version.Create(
|
||||
new CreateVersionInput(rootObjectId, model.id, AutomationRunData.ProjectId, versionMessage),
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
AutomationResult.ResultVersions.Add(newVersion.id);
|
||||
|
||||
return newVersion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set context view for automation result view.
|
||||
/// </summary>
|
||||
/// <param name="resourceIds"> Resource contexts to bind into view.</param>
|
||||
/// <param name="includeSourceModelVersion"> Whether bind source version into result view or not.</param>
|
||||
/// <exception cref="SpeckleException"> Throws if there is no context to create result view.</exception>
|
||||
[MemberNotNull(nameof(ContextView))]
|
||||
[AutoInterfaceIgnore] //Ignore so we can explicitly add the MemberNotNull attibute to the interface method
|
||||
public void SetContextView(IReadOnlyCollection<string>? resourceIds = null, bool includeSourceModelVersion = true)
|
||||
{
|
||||
List<string> linkResources = new();
|
||||
if (includeSourceModelVersion)
|
||||
{
|
||||
foreach (var trigger in AutomationRunData.Triggers)
|
||||
{
|
||||
switch (trigger)
|
||||
{
|
||||
case VersionCreationTrigger versionCreationTrigger:
|
||||
{
|
||||
linkResources.Add($"{versionCreationTrigger.Payload.ModelId}@{versionCreationTrigger.Payload.VersionId}");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new SpeckleException($"Could not link resource specified by {trigger.TriggerType} trigger");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (resourceIds is not null)
|
||||
{
|
||||
linkResources.AddRange(resourceIds);
|
||||
}
|
||||
|
||||
if (linkResources.Count == 0)
|
||||
{
|
||||
throw new SpeckleException("We do not have enough resource ids to compose a context view");
|
||||
}
|
||||
|
||||
ContextView = $"/projects/{AutomationRunData.ProjectId}/models/{string.Join(",", linkResources)}";
|
||||
}
|
||||
|
||||
public async Task ReportRunStatus()
|
||||
{
|
||||
ObjectResults? objectResults = null;
|
||||
if (RunStatus is "SUCCEEDED" or "FAILED")
|
||||
{
|
||||
objectResults = new ObjectResults
|
||||
{
|
||||
Version = 2,
|
||||
Values = new ObjectResultValues
|
||||
{
|
||||
BlobIds = AutomationResult.Blobs,
|
||||
ObjectResults = AutomationResult.ObjectResults,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
//language=graphql
|
||||
const string QUERY = """
|
||||
mutation AutomateFunctionRunStatusReport($projectId: String!, $functionRunId: String!, $status: AutomateRunStatus!, $statusMessage: String, $results: JSONObject, $contextView: String) {
|
||||
automateFunctionRunStatusReport(
|
||||
input: {projectId: $projectId, functionRunId: $functionRunId, status: $status, statusMessage: $statusMessage, contextView: $contextView, results: $results}
|
||||
)
|
||||
}
|
||||
""";
|
||||
GraphQLRequest request = new()
|
||||
{
|
||||
Query = QUERY,
|
||||
Variables = new
|
||||
{
|
||||
projectId = AutomationRunData.ProjectId,
|
||||
functionRunId = AutomationRunData.FunctionRunId,
|
||||
status = RunStatus,
|
||||
statusMessage = AutomationResult.StatusMessage,
|
||||
contextView = ContextView,
|
||||
results = objectResults,
|
||||
},
|
||||
};
|
||||
await SpeckleClient.ExecuteGraphQLRequest<Dictionary<string, object>>(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores result file in automation result. It will be available to download on Frontend if added.
|
||||
/// </summary>
|
||||
/// <param name="filePath"> File path to store.</param>
|
||||
/// <exception cref="FileNotFoundException"> Throws if given file path is not exist.</exception>
|
||||
/// <exception cref="SpeckleException"> Throws if upload requests return no result.</exception>
|
||||
public async Task StoreFileResult(string filePath)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
throw new FileNotFoundException("The given file path doesn't exist", fileName: filePath);
|
||||
}
|
||||
|
||||
using MultipartFormDataContent formData = new();
|
||||
FileStream fileStream = new(filePath, FileMode.Open, FileAccess.Read);
|
||||
using StreamContent streamContent = new(fileStream);
|
||||
formData.Add(streamContent, "files", Path.GetFileName(filePath));
|
||||
HttpResponseMessage request = await SpeckleClient
|
||||
.GQLClient.HttpClient.PostAsync(
|
||||
new Uri(AutomationRunData.SpeckleServerUrl, $"api/stream/{AutomationRunData.ProjectId}/blob"),
|
||||
formData
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
request.EnsureSuccessStatusCode();
|
||||
string responseString = await request.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
Console.WriteLine("RESPONSE - " + responseString);
|
||||
BlobUploadResponse uploadResponse = JsonConvert.DeserializeObject<BlobUploadResponse>(responseString);
|
||||
if (uploadResponse.UploadResults.Count != 1)
|
||||
{
|
||||
throw new SpeckleException("Expected one upload result.");
|
||||
}
|
||||
|
||||
AutomationResult.Blobs.AddRange(uploadResponse.UploadResults.Select(r => r.BlobId));
|
||||
}
|
||||
|
||||
private void MarkRun(AutomationStatus status, string? statusMessage)
|
||||
{
|
||||
double duration = Elapsed.TotalSeconds;
|
||||
AutomationResult.StatusMessage = statusMessage;
|
||||
string statusValue = AutomationStatusMapping.Get(status);
|
||||
AutomationResult.RunStatus = statusValue;
|
||||
AutomationResult.Elapsed = duration;
|
||||
|
||||
string msg = $"Automation run {statusValue} after {duration} seconds.";
|
||||
if (statusMessage is not null)
|
||||
{
|
||||
msg += $"\n{statusMessage}";
|
||||
}
|
||||
|
||||
Console.WriteLine(msg);
|
||||
}
|
||||
|
||||
public void MarkRunFailed(string statusMessage) => MarkRun(AutomationStatus.Failed, statusMessage);
|
||||
|
||||
public void MarkRunException(string? statusMessage) => MarkRun(AutomationStatus.Exception, statusMessage);
|
||||
|
||||
public void MarkRunSuccess(string? statusMessage) => MarkRun(AutomationStatus.Succeeded, statusMessage);
|
||||
|
||||
/// <summary>
|
||||
/// Add a new error case to the run results.
|
||||
/// </summary>
|
||||
/// <param name="category">A short tag for the error type.</param>
|
||||
/// <param name="affectedObjects">A list of objects that are causing the result.</param>
|
||||
/// <param name="message">Optional error message.</param>
|
||||
/// <param name="metadata">User provided metadata key value pairs.</param>
|
||||
/// <param name="visualOverrides">Case specific 3D visual overrides.</param>
|
||||
/// <exception cref="ArgumentException">Throws if the provided <paramref name="affectedObjects"/> input is empty.</exception>
|
||||
public void AttachErrorToObjects(
|
||||
string category,
|
||||
IReadOnlyCollection<Base> affectedObjects,
|
||||
string? message = null,
|
||||
Dictionary<string, object>? metadata = null,
|
||||
Dictionary<string, object>? visualOverrides = null
|
||||
) => AttachResultToObjects(ObjectResultLevel.Error, category, affectedObjects, message, metadata, visualOverrides);
|
||||
|
||||
/// <summary>
|
||||
/// Add a new warning case to the run results.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="AttachErrorToObjects"/>
|
||||
public void AttachWarningToObjects(
|
||||
string category,
|
||||
IReadOnlyCollection<Base> affectedObjects,
|
||||
string? message = null,
|
||||
Dictionary<string, object>? metadata = null,
|
||||
Dictionary<string, object>? visualOverrides = null
|
||||
) => AttachResultToObjects(ObjectResultLevel.Warning, category, affectedObjects, message, metadata, visualOverrides);
|
||||
|
||||
/// <summary>
|
||||
/// Add a new info case to the run results.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="AttachErrorToObjects"/>
|
||||
public void AttachInfoToObjects(
|
||||
string category,
|
||||
IReadOnlyCollection<Base> affectedObjects,
|
||||
string? message = null,
|
||||
Dictionary<string, object>? metadata = null,
|
||||
Dictionary<string, object>? visualOverrides = null
|
||||
) => AttachResultToObjects(ObjectResultLevel.Info, category, affectedObjects, message, metadata, visualOverrides);
|
||||
|
||||
/// <summary>
|
||||
/// Add a new success case to the run results.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="AttachErrorToObjects"/>
|
||||
public void AttachSuccessToObjects(
|
||||
string category,
|
||||
IReadOnlyCollection<Base> affectedObjects,
|
||||
string? message = null,
|
||||
Dictionary<string, object>? metadata = null,
|
||||
Dictionary<string, object>? visualOverrides = null
|
||||
) => AttachResultToObjects(ObjectResultLevel.Success, category, affectedObjects, message, metadata, visualOverrides);
|
||||
|
||||
/// <summary>
|
||||
/// Add a new case to the run results.
|
||||
/// </summary>
|
||||
/// <param name="level">The level assigned to this result.</param>
|
||||
/// <inheritdoc cref="AttachErrorToObjects"/>
|
||||
public void AttachResultToObjects(
|
||||
ObjectResultLevel level,
|
||||
string category,
|
||||
IReadOnlyCollection<Base> affectedObjects,
|
||||
string? message = null,
|
||||
Dictionary<string, object>? metadata = null,
|
||||
Dictionary<string, object>? visualOverrides = null
|
||||
)
|
||||
{
|
||||
if (affectedObjects.Count == 0)
|
||||
{
|
||||
throw new ArgumentException($"Need at least one affected object to report a(n) {level}");
|
||||
}
|
||||
|
||||
string levelString = ObjectResultLevelMapping.Get(level);
|
||||
Dictionary<string, string?> ids = affectedObjects.ToDictionary(
|
||||
x => x.id.NotNull($"You can only attach {level} results to objects with an id"),
|
||||
x => x.applicationId
|
||||
);
|
||||
|
||||
Console.WriteLine($"Created new {levelString.ToUpper()} category: {category} caused by: {message}");
|
||||
|
||||
ResultCase resultCase = new()
|
||||
{
|
||||
Category = category,
|
||||
Level = levelString,
|
||||
ObjectAppIds = ids,
|
||||
Message = message,
|
||||
Metadata = metadata,
|
||||
VisualOverrides = visualOverrides,
|
||||
};
|
||||
|
||||
AutomationResult.ObjectResults.Add(resultCase);
|
||||
}
|
||||
}
|
||||
|
||||
public partial interface IAutomationContext
|
||||
{
|
||||
[MemberNotNull(nameof(ContextView))]
|
||||
public void SetContextView(IReadOnlyCollection<string>? resourceIds = null, bool includeSourceModelVersion = true);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Speckle.Automate.Sdk.DataAnnotations;
|
||||
|
||||
/// <summary>
|
||||
/// If specified, the given function input will be redacted in all contexts.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.All)]
|
||||
public sealed class SecretAttribute : Attribute { }
|
||||
@@ -0,0 +1,195 @@
|
||||
using System.CommandLine;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Schema;
|
||||
using Newtonsoft.Json.Schema.Generation;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Speckle.Automate.Sdk.DataAnnotations;
|
||||
using Speckle.Automate.Sdk.Schema;
|
||||
using Speckle.InterfaceGenerator;
|
||||
using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Automate.Sdk;
|
||||
|
||||
/// <summary>
|
||||
/// Provides mechanisms to execute any function that conforms to the AutomateFunction "interface"
|
||||
/// </summary>
|
||||
[GenerateAutoInterface(VisibilityModifier = "public")]
|
||||
internal class AutomationRunner(IAutomationContextFactory contextFactory) : IAutomationRunner
|
||||
{
|
||||
[SuppressMessage("Design", "CA1031:Do not catch general exception types")]
|
||||
public async Task<IAutomationContext> RunFunction<TInput>(
|
||||
Func<IAutomationContext, TInput, Task> automateFunction,
|
||||
AutomationRunData automationRunData,
|
||||
string speckleToken,
|
||||
TInput inputs
|
||||
)
|
||||
where TInput : struct
|
||||
{
|
||||
var automationContext = await contextFactory.Initialize(automationRunData, speckleToken).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
await automateFunction.Invoke(automationContext, inputs).ConfigureAwait(false);
|
||||
if (automationContext.RunStatus is not ("FAILED" or "SUCCEEDED"))
|
||||
{
|
||||
automationContext.MarkRunSuccess(
|
||||
"WARNING: Automate assumed a success status, but it was not marked as so by the function."
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
automationContext.MarkRunException("Function error. Check the automation run logs for details.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (automationContext.ContextView is null)
|
||||
{
|
||||
automationContext.SetContextView();
|
||||
}
|
||||
|
||||
await automationContext.ReportRunStatus().ConfigureAwait(false);
|
||||
}
|
||||
return automationContext;
|
||||
}
|
||||
|
||||
public async Task<IAutomationContext> RunFunction(
|
||||
Func<IAutomationContext, Task> automateFunction,
|
||||
AutomationRunData automationRunData,
|
||||
string speckleToken
|
||||
) =>
|
||||
await RunFunction(
|
||||
async (context, _) => await automateFunction(context).ConfigureAwait(false),
|
||||
automationRunData,
|
||||
speckleToken,
|
||||
new Fake()
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
private struct Fake { }
|
||||
|
||||
/// <summary>
|
||||
/// Main entrypoint to execute an Automate function with no input data
|
||||
/// </summary>
|
||||
/// <param name="args">The command line arguments passed into the function by automate</param>
|
||||
/// <param name="automateFunction">The automate function to execute</param>
|
||||
/// <remarks>This should always be called in your own functions, as it contains the logic to trigger the function automatically.</remarks>
|
||||
public async Task<int> Main(string[] args, Func<IAutomationContext, Task> automateFunction)
|
||||
{
|
||||
return await Main(
|
||||
args,
|
||||
async (IAutomationContext context, Fake _) => await automateFunction(context).ConfigureAwait(false)
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main entrypoint to execute an Automate function with input data of type <typeparamref name="TInput"/>.
|
||||
/// </summary>
|
||||
/// <param name="args">The command line arguments passed into the function by automate</param>
|
||||
/// <param name="automateFunction">The automate function to execute</param>
|
||||
/// <typeparam name="TInput">The provided input data</typeparam>
|
||||
/// <remarks>This should always be called in your own functions, as it contains the logic to trigger the function automatically.</remarks>
|
||||
public async Task<int> Main<TInput>(string[] args, Func<IAutomationContext, TInput, Task> automateFunction)
|
||||
where TInput : struct
|
||||
{
|
||||
Argument<string> pathArg = new(name: "Input Path", description: "A file path to retrieve function inputs");
|
||||
RootCommand rootCommand = new();
|
||||
|
||||
// a stupid hack to be able to exit with a specific integer exit code
|
||||
// read more at https://github.com/dotnet/command-line-api/issues/1570
|
||||
var exitCode = 0;
|
||||
|
||||
rootCommand.AddArgument(pathArg);
|
||||
rootCommand.SetHandler(
|
||||
async inputPath =>
|
||||
{
|
||||
try
|
||||
{
|
||||
FunctionRunData<TInput> data = FunctionRunDataParser.FromPath<TInput>(inputPath);
|
||||
|
||||
var context = await RunFunction(
|
||||
automateFunction,
|
||||
data.AutomationRunData,
|
||||
data.SpeckleToken,
|
||||
data.FunctionInputs
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (context.RunStatus is "EXCEPTION")
|
||||
{
|
||||
exitCode = 1;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
exitCode = 1;
|
||||
throw;
|
||||
}
|
||||
},
|
||||
pathArg
|
||||
);
|
||||
|
||||
Argument<string> schemaFilePathArg = new(
|
||||
name: "Function inputs file path",
|
||||
description: "A token to talk to the Speckle server with"
|
||||
);
|
||||
|
||||
Command generateSchemaCommand = new("generate-schema", "Generate JSON schema for the function inputs");
|
||||
generateSchemaCommand.AddArgument(schemaFilePathArg);
|
||||
generateSchemaCommand.SetHandler(
|
||||
schemaFilePath =>
|
||||
{
|
||||
try
|
||||
{
|
||||
JSchemaGenerator generator = new() { ContractResolver = new CamelCasePropertyNamesContractResolver() };
|
||||
generator.GenerationProviders.Add(new SpeckleSecretProvider());
|
||||
JSchema schema = generator.Generate(typeof(TInput));
|
||||
schema.ToString(SchemaVersion.Draft2019_09);
|
||||
File.WriteAllText(schemaFilePath, schema.ToString());
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
exitCode = 1;
|
||||
throw;
|
||||
}
|
||||
},
|
||||
schemaFilePathArg
|
||||
);
|
||||
rootCommand.Add(generateSchemaCommand);
|
||||
|
||||
await rootCommand.InvokeAsync(args).ConfigureAwait(false);
|
||||
|
||||
// if we've gotten this far, the execution should technically be completed as expected
|
||||
// thus exiting with 0 is the semantically correct thing to do
|
||||
return exitCode;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SpeckleSecretProvider : JSchemaGenerationProvider
|
||||
{
|
||||
public override JSchema? GetSchema(JSchemaTypeGenerationContext context)
|
||||
{
|
||||
var attributes = context.MemberProperty?.AttributeProvider?.GetAttributes(false) ?? new List<Attribute>();
|
||||
var isSecretString = attributes.Any(att => att is SecretAttribute);
|
||||
|
||||
if (isSecretString)
|
||||
{
|
||||
return CreateSchemaWithWriteOnly(context.ObjectType, context.Required);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static JSchema CreateSchemaWithWriteOnly(Type type, Required required)
|
||||
{
|
||||
JSchemaGenerator generator = new();
|
||||
JSchema schema = generator.Generate(type, required != Required.Always);
|
||||
|
||||
schema.WriteOnly = true;
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Speckle.Automate.Sdk.Schema;
|
||||
|
||||
public class AutomationResult
|
||||
{
|
||||
public double Elapsed { get; set; }
|
||||
public string? ResultView { get; set; }
|
||||
public List<string> ResultVersions { get; set; } = new();
|
||||
public List<string> Blobs { get; set; } = new();
|
||||
public string RunStatus { get; set; } = AutomationStatusMapping.Get(AutomationStatus.Running);
|
||||
public string? StatusMessage { get; set; }
|
||||
public List<ResultCase> ObjectResults { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Speckle.Automate.Sdk.Schema.Triggers;
|
||||
|
||||
namespace Speckle.Automate.Sdk.Schema;
|
||||
|
||||
///<summary>
|
||||
/// Values of the project, model and automation that triggered this function run.
|
||||
///</summary>
|
||||
public readonly struct AutomationRunData
|
||||
{
|
||||
[JsonRequired]
|
||||
public required string ProjectId { get; init; }
|
||||
|
||||
[JsonRequired]
|
||||
public required Uri SpeckleServerUrl { get; init; }
|
||||
|
||||
[JsonRequired]
|
||||
public required string AutomationId { get; init; }
|
||||
|
||||
[JsonRequired]
|
||||
public required string AutomationRunId { get; init; }
|
||||
|
||||
[JsonRequired]
|
||||
public required string FunctionRunId { get; init; }
|
||||
|
||||
[JsonRequired]
|
||||
public required List<VersionCreationTrigger> Triggers { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Speckle.Automate.Sdk.Schema;
|
||||
|
||||
///<summary>
|
||||
/// Set the status of the automation.
|
||||
///</summary>
|
||||
public enum AutomationStatus
|
||||
{
|
||||
Initializing,
|
||||
Running,
|
||||
Failed,
|
||||
Succeeded,
|
||||
Exception,
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace Speckle.Automate.Sdk.Schema;
|
||||
|
||||
public abstract class AutomationStatusMapping
|
||||
{
|
||||
private const string INITIALIZING = "INITIALIZING";
|
||||
private const string RUNNING = "RUNNING";
|
||||
private const string FAILED = "FAILED";
|
||||
private const string SUCCEEDED = "SUCCEEDED";
|
||||
private const string EXCEPTION = "EXCEPTION";
|
||||
|
||||
public static string Get(AutomationStatus status) =>
|
||||
status switch
|
||||
{
|
||||
AutomationStatus.Running => RUNNING,
|
||||
AutomationStatus.Failed => FAILED,
|
||||
AutomationStatus.Succeeded => SUCCEEDED,
|
||||
AutomationStatus.Initializing => INITIALIZING,
|
||||
AutomationStatus.Exception => EXCEPTION,
|
||||
_ => throw new ArgumentOutOfRangeException($"Not valid value for enum {status}"),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Speckle.Automate.Sdk.Schema;
|
||||
|
||||
public readonly struct BlobUploadResponse
|
||||
{
|
||||
public required List<UploadResult> UploadResults { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Speckle.Automate.Sdk.Schema;
|
||||
|
||||
/// <summary>
|
||||
/// Required data to run a function.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"> Type for <see cref="FunctionInputs"/>.</typeparam>
|
||||
public sealed class FunctionRunData<T>
|
||||
{
|
||||
[JsonRequired]
|
||||
public required string SpeckleToken { get; init; }
|
||||
|
||||
[JsonRequired]
|
||||
public required AutomationRunData AutomationRunData { get; init; }
|
||||
|
||||
public required T? FunctionInputs { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Speckle.Automate.Sdk.Schema;
|
||||
|
||||
public static class FunctionRunDataParser
|
||||
{
|
||||
private static readonly JsonSerializerOptions s_jsonSerializerSettings = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Function run data parser from json file path./>
|
||||
/// </summary>
|
||||
/// <param name="inputLocation"> Path to retrieve function run data.</param>
|
||||
/// <typeparam name="T"> Type for function inputs.</typeparam>
|
||||
/// <returns>The data to be able to run function.</returns>
|
||||
/// <exception cref="JsonException">Json was not valid</exception>
|
||||
/// <exception cref="FileNotFoundException"> Throws unless file exists.</exception>
|
||||
public static FunctionRunData<T> FromPath<T>(string inputLocation)
|
||||
{
|
||||
string inputJsonString = ReadInputData(inputLocation);
|
||||
//It's important to use System.Text.Json here. The template FunctionInputs are decorated with STJ attributes
|
||||
FunctionRunData<T>? functionRunData = JsonSerializer.Deserialize<FunctionRunData<T>>(
|
||||
inputJsonString,
|
||||
s_jsonSerializerSettings
|
||||
);
|
||||
|
||||
if (functionRunData is null)
|
||||
{
|
||||
throw new JsonException($"Function run data couldn't deserialized at {inputLocation}");
|
||||
}
|
||||
|
||||
return functionRunData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read text from file.
|
||||
/// </summary>
|
||||
/// <param name="inputLocation"> Path to check file is exist.</param>
|
||||
/// <returns>Text in file.</returns>
|
||||
/// <exception cref="FileNotFoundException"> Throws unless file exists.</exception>
|
||||
private static string ReadInputData(string inputLocation)
|
||||
{
|
||||
if (!File.Exists(inputLocation))
|
||||
{
|
||||
throw new FileNotFoundException($"Cannot find the function inputs file at {inputLocation}");
|
||||
}
|
||||
|
||||
return File.ReadAllText(inputLocation);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Speckle.Automate.Sdk.Schema;
|
||||
|
||||
public enum ObjectResultLevel
|
||||
{
|
||||
Success,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Speckle.Automate.Sdk.Schema;
|
||||
|
||||
public abstract class ObjectResultLevelMapping
|
||||
{
|
||||
private const string SUCCESS = "SUCCESS";
|
||||
private const string INFO = "INFO";
|
||||
private const string WARNING = "WARNING";
|
||||
private const string ERROR = "ERROR";
|
||||
|
||||
public static string Get(ObjectResultLevel level) =>
|
||||
level switch
|
||||
{
|
||||
ObjectResultLevel.Error => ERROR,
|
||||
ObjectResultLevel.Warning => WARNING,
|
||||
ObjectResultLevel.Info => INFO,
|
||||
ObjectResultLevel.Success => SUCCESS,
|
||||
_ => throw new ArgumentOutOfRangeException($"Not valid value for enum {level}"),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Speckle.Automate.Sdk.Schema;
|
||||
|
||||
public readonly struct ObjectResultValues
|
||||
{
|
||||
public required List<ResultCase> ObjectResults { get; init; }
|
||||
public required List<string> BlobIds { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Speckle.Automate.Sdk.Schema;
|
||||
|
||||
public readonly struct ObjectResults
|
||||
{
|
||||
public int Version { get; init; }
|
||||
public ObjectResultValues Values { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Speckle.Automate.Sdk.Schema;
|
||||
|
||||
public readonly struct ResultCase
|
||||
{
|
||||
public required string Category { get; init; }
|
||||
public required string Level { get; init; }
|
||||
public required Dictionary<string, string?> ObjectAppIds { get; init; }
|
||||
public required string? Message { get; init; }
|
||||
public required Dictionary<string, object>? Metadata { get; init; }
|
||||
public required Dictionary<string, object>? VisualOverrides { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Speckle.Automate.Sdk.Schema.Triggers;
|
||||
|
||||
public abstract class AutomationRunTriggerBase
|
||||
{
|
||||
public required string TriggerType { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Speckle.Automate.Sdk.Schema.Triggers;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single version creation trigger for the automation run.
|
||||
/// </summary>
|
||||
public sealed class VersionCreationTrigger : AutomationRunTriggerBase
|
||||
{
|
||||
public const string VERSION_CREATION_TRIGGER_TYPE = "versionCreation";
|
||||
|
||||
[JsonRequired]
|
||||
public required VersionCreationTriggerPayload Payload { get; init; }
|
||||
|
||||
public VersionCreationTrigger() { }
|
||||
|
||||
[SetsRequiredMembers]
|
||||
public VersionCreationTrigger(string modelId, string versionId)
|
||||
{
|
||||
Payload = new() { ModelId = modelId, VersionId = versionId };
|
||||
TriggerType = VERSION_CREATION_TRIGGER_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the version creation trigger payload.
|
||||
/// </summary>
|
||||
public sealed record VersionCreationTriggerPayload
|
||||
{
|
||||
[JsonRequired]
|
||||
public required string ModelId { get; init; }
|
||||
|
||||
[JsonRequired]
|
||||
public required string VersionId { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Speckle.Automate.Sdk.Schema;
|
||||
|
||||
public readonly struct UploadResult
|
||||
{
|
||||
public required string BlobId { get; init; }
|
||||
public required string FileName { get; init; }
|
||||
public required int UploadStatus { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Serialisation.V2;
|
||||
|
||||
namespace Speckle.Automate.Sdk;
|
||||
|
||||
public static class ServiceRegistration
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets-up the serviceCollection with all the services in Speckle.Automate.Sdk and Speckle.Sdk
|
||||
/// </summary>
|
||||
/// <param name="serviceCollection"></param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddAutomateSdk(this IServiceCollection serviceCollection)
|
||||
{
|
||||
var executingAssembly = Assembly.GetExecutingAssembly().GetName();
|
||||
var speckleAssembly = typeof(Base).Assembly.GetName();
|
||||
AddAutomateSdk(
|
||||
serviceCollection,
|
||||
new SpeckleSdkOptions(
|
||||
new(executingAssembly.FullName, "automatefunction"),
|
||||
executingAssembly.Version?.ToString() ?? "Unknown",
|
||||
speckleAssembly.Version?.ToString(),
|
||||
[typeof(Base).Assembly, typeof(Point).Assembly]
|
||||
)
|
||||
);
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddAutomateSdk(
|
||||
this IServiceCollection serviceCollection,
|
||||
SpeckleSdkOptions speckleSdkOptions
|
||||
)
|
||||
{
|
||||
serviceCollection.AddSpeckleSdk(speckleSdkOptions);
|
||||
|
||||
//Overwrite the SDK's default IDeserializeProcessFactory to ensure SQLite is not used to cache objects
|
||||
serviceCollection.AddTransient<IDeserializeProcessFactory, DeserializeProcessFactoryNoCache>();
|
||||
|
||||
//Add automate assembly services
|
||||
serviceCollection.AddTransient<IAutomationContextFactory, AutomationContextFactory>();
|
||||
serviceCollection.AddTransient<IAutomationRunner, AutomationRunner>();
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup Label="Compiler Properties">
|
||||
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Nugetspec Package Properties">
|
||||
<PackageId>Speckle.Automate.Sdk</PackageId>
|
||||
<Description>Speckle Automate SDK</Description>
|
||||
<PackageTags>$(PackageTags) speckle automation</PackageTags>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Nuget Package Properties">
|
||||
<IsPackable>true</IsPackable>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Expose internals to test projects">
|
||||
<InternalsVisibleTo Include="Speckle.Automate.Sdk.Tests.Integration" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Newtonsoft.Json.Schema" />
|
||||
<PackageReference Include="System.CommandLine" NoWarn="NU5104" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
|
||||
<PackageReference Include="System.Text.Json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\Speckle.Objects\Speckle.Objects.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,79 @@
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Automate.Sdk.Test;
|
||||
|
||||
public class TestAppSettings
|
||||
{
|
||||
public string? SpeckleToken { get; set; }
|
||||
public string? SpeckleServerUrl { get; set; }
|
||||
public string? SpeckleProjectId { get; set; }
|
||||
public string? SpeckleAutomationId { get; set; }
|
||||
}
|
||||
|
||||
public static class TestAutomateEnvironment
|
||||
{
|
||||
public static TestAppSettings? AppSettings { get; private set; }
|
||||
|
||||
private static string GetEnvironmentVariable(string environmentVariableName)
|
||||
{
|
||||
var value = TryGetEnvironmentVariable(environmentVariableName);
|
||||
|
||||
if (value is null)
|
||||
{
|
||||
throw new SpeckleException($"Cannot run tests without a {environmentVariableName} environment variable");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static string? TryGetEnvironmentVariable(string environmentVariableName)
|
||||
{
|
||||
return Environment.GetEnvironmentVariable(environmentVariableName);
|
||||
}
|
||||
|
||||
private static TestAppSettings? GetAppSettings()
|
||||
{
|
||||
if (AppSettings != null)
|
||||
{
|
||||
return AppSettings;
|
||||
}
|
||||
|
||||
var path = "./appsettings.json";
|
||||
var json = File.ReadAllText(path);
|
||||
|
||||
var appSettings = JsonConvert.DeserializeObject<TestAppSettings>(json);
|
||||
|
||||
AppSettings = appSettings;
|
||||
|
||||
return AppSettings;
|
||||
}
|
||||
|
||||
public static string GetSpeckleToken()
|
||||
{
|
||||
return GetAppSettings()?.SpeckleToken ?? GetEnvironmentVariable("SPECKLE_TOKEN");
|
||||
}
|
||||
|
||||
public static Uri GetSpeckleServerUrl()
|
||||
{
|
||||
var urlString =
|
||||
GetAppSettings()?.SpeckleServerUrl ?? TryGetEnvironmentVariable("SPECKLE_SERVER_URL") ?? "http://127.0.0.1:3000";
|
||||
|
||||
return new Uri(urlString);
|
||||
}
|
||||
|
||||
public static string GetSpeckleProjectId()
|
||||
{
|
||||
return GetAppSettings()?.SpeckleProjectId ?? GetEnvironmentVariable("SPECKLE_PROJECT_ID");
|
||||
}
|
||||
|
||||
public static string GetSpeckleAutomationId()
|
||||
{
|
||||
return GetAppSettings()?.SpeckleAutomationId ?? GetEnvironmentVariable("SPECKLE_AUTOMATION_ID");
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
AppSettings = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using GraphQL;
|
||||
using Speckle.Automate.Sdk.Schema;
|
||||
using Speckle.Automate.Sdk.Schema.Triggers;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Models.Responses;
|
||||
|
||||
namespace Speckle.Automate.Sdk.Test;
|
||||
|
||||
internal class TestAutomationRun
|
||||
{
|
||||
[JsonRequired]
|
||||
public required string AutomationRunId { get; init; }
|
||||
|
||||
[JsonRequired]
|
||||
public required string FunctionRunId { get; init; }
|
||||
|
||||
[JsonRequired]
|
||||
public required IReadOnlyList<TestAutomationRunTrigger> Triggers { get; init; }
|
||||
}
|
||||
|
||||
internal class TestAutomationRunTrigger : AutomationRunTriggerBase
|
||||
{
|
||||
/// <remarks>This should really be a TestAutomationRunTriggerPayload, but right now, they look the samee</remarks>
|
||||
public required VersionCreationTriggerPayload Payload { get; init; }
|
||||
}
|
||||
|
||||
public static class TestAutomateUtils
|
||||
{
|
||||
public static async Task<AutomationRunData> CreateTestRun(
|
||||
IClient speckleClient,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
//language=graphql
|
||||
const string QUERY = """
|
||||
mutation Mutation($projectId: ID!, $automationId: ID!) {
|
||||
data:projectMutations {
|
||||
data:automationMutations(projectId: $projectId) {
|
||||
data:createTestAutomationRun(automationId: $automationId) {
|
||||
automationRunId
|
||||
functionRunId
|
||||
triggers {
|
||||
payload {
|
||||
modelId
|
||||
versionId
|
||||
}
|
||||
triggerType
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
GraphQLRequest request = new(
|
||||
query: QUERY,
|
||||
variables: new
|
||||
{
|
||||
automationId = TestAutomateEnvironment.GetSpeckleAutomationId(),
|
||||
projectId = TestAutomateEnvironment.GetSpeckleProjectId(),
|
||||
}
|
||||
);
|
||||
|
||||
var res = await speckleClient
|
||||
.ExecuteGraphQLRequest<RequiredResponse<RequiredResponse<RequiredResponse<TestAutomationRun>>>>(
|
||||
request,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var runData = res.data.data.data;
|
||||
var triggerData = runData.Triggers[0].Payload;
|
||||
|
||||
string modelId = triggerData.ModelId;
|
||||
string versionId = triggerData.VersionId;
|
||||
|
||||
var data = new AutomationRunData()
|
||||
{
|
||||
ProjectId = TestAutomateEnvironment.GetSpeckleProjectId(),
|
||||
SpeckleServerUrl = TestAutomateEnvironment.GetSpeckleServerUrl(),
|
||||
AutomationId = TestAutomateEnvironment.GetSpeckleAutomationId(),
|
||||
AutomationRunId = runData.AutomationRunId,
|
||||
FunctionRunId = runData.FunctionRunId,
|
||||
Triggers = [new(modelId: modelId, versionId: versionId)],
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,618 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dependencies": {
|
||||
".NETStandard,Version=v2.0": {
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Build.Tasks.Git": "8.0.0",
|
||||
"Microsoft.SourceLink.Common": "8.0.0"
|
||||
}
|
||||
},
|
||||
"NETStandard.Library": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.0.3, )",
|
||||
"resolved": "2.0.3",
|
||||
"contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.1.0"
|
||||
}
|
||||
},
|
||||
"Newtonsoft.Json.Schema": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.0.1, )",
|
||||
"resolved": "4.0.1",
|
||||
"contentHash": "rbHUKp5WTIbqmLEeJ21nTTDGcfR0LA7bVMzm0bYc3yx6NFKiCIHzzvYbwA4Sqgs7+wNldc5nBlkbithWj8IZig==",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "13.0.3"
|
||||
}
|
||||
},
|
||||
"PolySharp": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.15.0, )",
|
||||
"resolved": "1.15.0",
|
||||
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
|
||||
},
|
||||
"Speckle.InterfaceGenerator": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.9.6, )",
|
||||
"resolved": "0.9.6",
|
||||
"contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w=="
|
||||
},
|
||||
"System.CommandLine": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.0.0-beta4.22272.1, )",
|
||||
"resolved": "2.0.0-beta4.22272.1",
|
||||
"contentHash": "1uqED/q2H0kKoLJ4+hI2iPSBSEdTuhfCYADeJrAqERmiGQ2NNacYKRNEQ+gFbU4glgVyK8rxI+ZOe1onEtr/Pg==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.4"
|
||||
}
|
||||
},
|
||||
"System.Text.Json": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.5, )",
|
||||
"resolved": "8.0.5",
|
||||
"contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "8.0.0",
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
|
||||
"System.Text.Encodings.Web": "8.0.0",
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
"GraphQL.Client.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Primitives": "6.0.0"
|
||||
}
|
||||
},
|
||||
"GraphQL.Client.Abstractions.Websocket": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client.Abstractions": "6.0.0"
|
||||
}
|
||||
},
|
||||
"GraphQL.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA=="
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.Data.Sqlite.Core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.5",
|
||||
"contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.core": "2.1.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Binder": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A=="
|
||||
},
|
||||
"Microsoft.Extensions.Options": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Primitives": "2.2.0",
|
||||
"System.ComponentModel.Annotations": "4.5.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.1",
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.NETCore.Platforms": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
|
||||
},
|
||||
"Microsoft.NETCore.Targets": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg=="
|
||||
},
|
||||
"Microsoft.SourceLink.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.3",
|
||||
"contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.lib.e_sqlite3": "2.1.4",
|
||||
"SQLitePCLRaw.provider.e_sqlite3": "2.1.4"
|
||||
}
|
||||
},
|
||||
"SQLitePCLRaw.core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.3"
|
||||
}
|
||||
},
|
||||
"SQLitePCLRaw.lib.e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg=="
|
||||
},
|
||||
"SQLitePCLRaw.provider.e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.core": "2.1.4"
|
||||
}
|
||||
},
|
||||
"System.Buffers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
|
||||
},
|
||||
"System.ComponentModel.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg=="
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.5",
|
||||
"contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Numerics.Vectors": "4.4.0",
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
}
|
||||
},
|
||||
"System.Numerics.Vectors": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
|
||||
},
|
||||
"System.Reactive": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==",
|
||||
"dependencies": {
|
||||
"System.Runtime.InteropServices.WindowsRuntime": "4.3.0",
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
"System.Runtime": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
|
||||
},
|
||||
"System.Runtime.InteropServices.WindowsRuntime": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "J4GUi3xZQLUBasNwZnjrffN8i5wpHrBtZoLG+OhRyGo/+YunMRWWtwoMDlUAIdmX0uRfpHIBDSV6zyr3yf00TA==",
|
||||
"dependencies": {
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encodings.Web": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Threading.Tasks.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.4",
|
||||
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
}
|
||||
},
|
||||
"speckle.objects": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.sdk": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "[6.0.0, )",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "[5.0.0, )",
|
||||
"Microsoft.CSharp": "[4.7.0, )",
|
||||
"Microsoft.Data.Sqlite": "[7.0.5, )",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0, )",
|
||||
"Microsoft.Extensions.Logging": "[2.2.0, )",
|
||||
"Speckle.DoubleNumerics": "[4.1.0, )",
|
||||
"Speckle.Newtonsoft.Json": "[13.0.2, )",
|
||||
"Speckle.Sdk.Dependencies": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.sdk.dependencies": {
|
||||
"type": "Project"
|
||||
},
|
||||
"GraphQL.Client": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[6.0.0, )",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client.Abstractions": "6.0.0",
|
||||
"GraphQL.Client.Abstractions.Websocket": "6.0.0",
|
||||
"System.Reactive": "5.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Bcl.AsyncInterfaces": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[5.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
|
||||
"dependencies": {
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.CSharp": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[4.7.0, )",
|
||||
"resolved": "4.7.0",
|
||||
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
|
||||
},
|
||||
"Microsoft.Data.Sqlite": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[7.0.5, )",
|
||||
"resolved": "7.0.5",
|
||||
"contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==",
|
||||
"dependencies": {
|
||||
"Microsoft.Data.Sqlite.Core": "7.0.5",
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Binder": "2.2.0",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Options": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Speckle.DoubleNumerics": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[4.1.0, )",
|
||||
"resolved": "4.1.0",
|
||||
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
|
||||
},
|
||||
"Speckle.Newtonsoft.Json": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[13.0.2, )",
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
}
|
||||
},
|
||||
"net8.0": {
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Build.Tasks.Git": "8.0.0",
|
||||
"Microsoft.SourceLink.Common": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Newtonsoft.Json.Schema": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.0.1, )",
|
||||
"resolved": "4.0.1",
|
||||
"contentHash": "rbHUKp5WTIbqmLEeJ21nTTDGcfR0LA7bVMzm0bYc3yx6NFKiCIHzzvYbwA4Sqgs7+wNldc5nBlkbithWj8IZig==",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "13.0.3"
|
||||
}
|
||||
},
|
||||
"PolySharp": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.15.0, )",
|
||||
"resolved": "1.15.0",
|
||||
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
|
||||
},
|
||||
"Speckle.InterfaceGenerator": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.9.6, )",
|
||||
"resolved": "0.9.6",
|
||||
"contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w=="
|
||||
},
|
||||
"System.CommandLine": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.0.0-beta4.22272.1, )",
|
||||
"resolved": "2.0.0-beta4.22272.1",
|
||||
"contentHash": "1uqED/q2H0kKoLJ4+hI2iPSBSEdTuhfCYADeJrAqERmiGQ2NNacYKRNEQ+gFbU4glgVyK8rxI+ZOe1onEtr/Pg=="
|
||||
},
|
||||
"GraphQL.Client.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Primitives": "6.0.0"
|
||||
}
|
||||
},
|
||||
"GraphQL.Client.Abstractions.Websocket": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client.Abstractions": "6.0.0"
|
||||
}
|
||||
},
|
||||
"GraphQL.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA=="
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.Data.Sqlite.Core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.5",
|
||||
"contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.core": "2.1.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Binder": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A=="
|
||||
},
|
||||
"Microsoft.Extensions.Options": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Primitives": "2.2.0",
|
||||
"System.ComponentModel.Annotations": "4.5.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.1",
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.3",
|
||||
"contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.lib.e_sqlite3": "2.1.4",
|
||||
"SQLitePCLRaw.provider.e_sqlite3": "2.1.4"
|
||||
}
|
||||
},
|
||||
"SQLitePCLRaw.core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.3"
|
||||
}
|
||||
},
|
||||
"SQLitePCLRaw.lib.e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg=="
|
||||
},
|
||||
"SQLitePCLRaw.provider.e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.core": "2.1.4"
|
||||
}
|
||||
},
|
||||
"System.ComponentModel.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg=="
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.3",
|
||||
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
|
||||
},
|
||||
"System.Reactive": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ=="
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
|
||||
},
|
||||
"speckle.objects": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.sdk": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "[6.0.0, )",
|
||||
"Microsoft.Data.Sqlite": "[7.0.5, )",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0, )",
|
||||
"Microsoft.Extensions.Logging": "[2.2.0, )",
|
||||
"Speckle.DoubleNumerics": "[4.1.0, )",
|
||||
"Speckle.Newtonsoft.Json": "[13.0.2, )",
|
||||
"Speckle.Sdk.Dependencies": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.sdk.dependencies": {
|
||||
"type": "Project"
|
||||
},
|
||||
"GraphQL.Client": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[6.0.0, )",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client.Abstractions": "6.0.0",
|
||||
"GraphQL.Client.Abstractions.Websocket": "6.0.0",
|
||||
"System.Reactive": "5.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Data.Sqlite": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[7.0.5, )",
|
||||
"resolved": "7.0.5",
|
||||
"contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==",
|
||||
"dependencies": {
|
||||
"Microsoft.Data.Sqlite.Core": "7.0.5",
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Binder": "2.2.0",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Options": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Speckle.DoubleNumerics": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[4.1.0, )",
|
||||
"resolved": "4.1.0",
|
||||
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
|
||||
},
|
||||
"Speckle.Newtonsoft.Json": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[13.0.2, )",
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
[*.{cs,vb}]
|
||||
|
||||
# Name properties with camelCase
|
||||
dotnet_naming_rule.properties_should_be_camel_case.severity = none
|
||||
dotnet_naming_rule.properties_should_be_camel_case.symbols = properties
|
||||
dotnet_naming_rule.properties_should_be_camel_case.style = property_style
|
||||
|
||||
dotnet_naming_symbols.properties.applicable_kinds = property
|
||||
dotnet_naming_style.property_style.capitalization = camel_case
|
||||
@@ -0,0 +1,68 @@
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Annotation;
|
||||
|
||||
/// <summary>
|
||||
/// Text class for representation in the viewer
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Annotation.Text")]
|
||||
public class Text : Base
|
||||
{
|
||||
/// <summary>
|
||||
/// Plain text, without formatting
|
||||
/// </summary>
|
||||
public required string value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Height in linear units or pixels (if Units.None)
|
||||
/// </summary>
|
||||
public required double height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Units will be 'Units.None' if the text size is defined in pixels (stays the same size
|
||||
/// independently of zooming the model). Default height in pixels is 17px (used for Viewer measurements)
|
||||
/// </summary>
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If true, the text is oriented to face the screen (camera-aligned).
|
||||
/// </summary>
|
||||
public required bool screenOriented { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Horizontal alignment: Left, Center or Right
|
||||
/// </summary>
|
||||
public AlignmentHorizontal alignmentH { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Vertical alignment: Top, Center or Bottom
|
||||
/// </summary>
|
||||
public AlignmentVertical alignmentV { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Plane axis vectors will be ignored if screenOriented is true
|
||||
/// </summary>
|
||||
public required Plane plane { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum width of the text field (in 'units').
|
||||
/// Text will be split into lines (wrapped) to fit into the width.
|
||||
/// null, if text should not be wrapped.
|
||||
/// </summary>
|
||||
public double? maxWidth { get; set; }
|
||||
}
|
||||
|
||||
public enum AlignmentHorizontal
|
||||
{
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
}
|
||||
|
||||
public enum AlignmentVertical
|
||||
{
|
||||
Top,
|
||||
Center,
|
||||
Bottom,
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a ArcGIS.Core.CoreObjectsBase object in ArcGIS
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Data.ArcgisObject")]
|
||||
public class ArcgisObject : DataObject, IGisObject
|
||||
{
|
||||
public required string type { get; set; }
|
||||
|
||||
public required string units { get; set; }
|
||||
|
||||
IReadOnlyList<Base> IDisplayValue<IReadOnlyList<Base>>.displayValue => displayValue;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a base class object in Archicad
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Data.ArchicadObject")]
|
||||
public class ArchicadObject : DataObject, IArchicadObject
|
||||
{
|
||||
public required string type { get; set; }
|
||||
|
||||
public required string level { get; set; }
|
||||
|
||||
[DetachProperty]
|
||||
public required List<ArchicadObject> elements { get; set; }
|
||||
|
||||
IReadOnlyList<IArchicadObject> IArchicadObject.elements => elements;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an Autodesk.Civil.DatabaseServices.Entity object in Civil3d
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Data.Civil3dObject")]
|
||||
public class Civil3dObject : DataObject, ICivilObject
|
||||
{
|
||||
public required string type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Curves representing the base curve of an entity
|
||||
/// </summary>
|
||||
public required List<ICurve>? baseCurves { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Children objects, eg profiles, this civil entity may contain.
|
||||
/// </summary>
|
||||
[DetachProperty]
|
||||
public required List<Base> elements { get; set; }
|
||||
|
||||
public required string units { get; set; }
|
||||
|
||||
IReadOnlyList<Base> ICivilObject.elements => elements;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Data;
|
||||
|
||||
[SpeckleType("Objects.Data.DataObject")]
|
||||
public class DataObject : Base, IDataObject
|
||||
{
|
||||
public required string name { get; set; }
|
||||
|
||||
[DetachProperty]
|
||||
public required List<Base> displayValue { get; set; }
|
||||
|
||||
public required Dictionary<string, object?> properties { get; set; }
|
||||
|
||||
IReadOnlyList<Base> IDisplayValue<IReadOnlyList<Base>>.displayValue => displayValue;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a wrapper object in ETABS
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Data.EtabsObject")]
|
||||
public class EtabsObject : DataObject, ICsiObject
|
||||
{
|
||||
public required string type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Children objects, eg joints, this etabs object may contain.
|
||||
/// </summary>
|
||||
[DetachProperty]
|
||||
public required List<EtabsObject> elements { get; set; }
|
||||
|
||||
public required string units { get; set; }
|
||||
|
||||
IReadOnlyList<ICsiObject> ICsiObject.elements => elements;
|
||||
|
||||
IReadOnlyList<Base> IDisplayValue<IReadOnlyList<Base>>.displayValue => displayValue;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a "first selectable ancestor" Navisworks.ModelItem object in Navisworks
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Data.NavisworksObject")]
|
||||
public class NavisworksObject : DataObject, INavisworksObject
|
||||
{
|
||||
public required string units { get; set; }
|
||||
|
||||
IReadOnlyList<Base> IDisplayValue<IReadOnlyList<Base>>.displayValue => displayValue;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an Autodesk.Revit.DB.Element object in Revit
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Data.RevitObject")]
|
||||
public class RevitObject : DataObject, IRevitObject
|
||||
{
|
||||
public required string type { get; set; }
|
||||
public required string family { get; set; }
|
||||
public required string category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The level constraint of the object.
|
||||
/// For objects constrained by multiple levels, this represents the base constraint.
|
||||
/// For objects with no level constraint, this should be null.
|
||||
/// </summary>
|
||||
public required string? level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A Curve or Point object representing the location of a Revit element.
|
||||
/// </summary>
|
||||
public required Base? location { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Children objects, eg hosted elements, this RevitObject may contain.
|
||||
/// </summary>
|
||||
[DetachProperty]
|
||||
public required List<RevitObject> elements { get; set; }
|
||||
|
||||
public required string units { get; set; }
|
||||
|
||||
IReadOnlyList<IRevitObject> IRevitObject.elements => elements;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an Tekla.Structures.Model.ModelObject object in Tekla Structures
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Data.TeklaObject")]
|
||||
public class TeklaObject : DataObject, ITeklaObject
|
||||
{
|
||||
public required string type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Children objects, eg profiles, this tekla modelobject may contain.
|
||||
/// </summary>
|
||||
[DetachProperty]
|
||||
public required List<TeklaObject> elements { get; set; }
|
||||
|
||||
public required string units { get; set; }
|
||||
|
||||
IReadOnlyList<ITeklaObject> ITeklaObject.elements => elements;
|
||||
|
||||
IReadOnlyList<Base> IDisplayValue<IReadOnlyList<Base>>.displayValue => displayValue;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Deprecated;
|
||||
|
||||
[SpeckleType("Objects.Deprecated.LegacyV2")]
|
||||
[DeprecatedSpeckleType("Objects.Other.BlockInstance")]
|
||||
[DeprecatedSpeckleType("Objects.Other.Revit.RevitInstance")]
|
||||
[DeprecatedSpeckleType("Objects.BuiltElements.View")]
|
||||
[DeprecatedSpeckleType("Objects.BuiltElements.GridLine")]
|
||||
[DeprecatedSpeckleType("Objects.Other.BlockDefinition")]
|
||||
[DeprecatedSpeckleType("Objects.Other.DisplayStyle")]
|
||||
[DeprecatedSpeckleType("Objects.Other.Material")]
|
||||
[DeprecatedSpeckleType("Objects.Other.MaterialQuantity")]
|
||||
[DeprecatedSpeckleType("Objects.Other.Revit.RevitMaterial")]
|
||||
[DeprecatedSpeckleType("Objects.BuiltElements.Revit.Parameter")]
|
||||
[DeprecatedSpeckleType("Objects.BuiltElements.Revit.Curve.ModelCurve")]
|
||||
[DeprecatedSpeckleType("Objects.BuiltElements.Revit.DirectShape")]
|
||||
public class LegacyV2 : Base { }
|
||||
@@ -0,0 +1,103 @@
|
||||
using Speckle.Objects.Geometry;
|
||||
|
||||
namespace Speckle.Objects;
|
||||
|
||||
public static class CurveTypeEncoding
|
||||
{
|
||||
public const double Arc = 0;
|
||||
public const double Circle = 1;
|
||||
public const double Curve = 2;
|
||||
public const double Ellipse = 3;
|
||||
public const double Line = 4;
|
||||
public const double Polyline = 5;
|
||||
public const double PolyCurve = 6;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class is a helper class for Brep curve encoding!!
|
||||
/// </summary>
|
||||
public static class CurveArrayEncodingExtensions
|
||||
{
|
||||
public static List<double> ToArray(IReadOnlyCollection<ICurve> curves)
|
||||
{
|
||||
var list = new List<double>();
|
||||
foreach (var curve in curves)
|
||||
{
|
||||
switch (curve)
|
||||
{
|
||||
case Arc a:
|
||||
list.AddRange(a.ToList());
|
||||
break;
|
||||
case Circle c:
|
||||
list.AddRange(c.ToList());
|
||||
break;
|
||||
case Curve c:
|
||||
list.AddRange(c.ToList());
|
||||
break;
|
||||
case Ellipse e:
|
||||
list.AddRange(e.ToList());
|
||||
break;
|
||||
case Line l:
|
||||
list.AddRange(l.ToList());
|
||||
break;
|
||||
case Polycurve p:
|
||||
list.AddRange(p.ToList());
|
||||
break;
|
||||
case Polyline p:
|
||||
list.AddRange(p.ToList());
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(curves), $"Unkown curve type: {curve.GetType()}.");
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<ICurve> FromArray(List<double> list)
|
||||
{
|
||||
var curves = new List<ICurve>();
|
||||
if (list.Count == 0)
|
||||
{
|
||||
return curves;
|
||||
}
|
||||
|
||||
var done = false;
|
||||
var currentIndex = 0;
|
||||
|
||||
while (!done)
|
||||
{
|
||||
var itemLength = (int)list[currentIndex];
|
||||
var item = list.GetRange(currentIndex, itemLength + 1);
|
||||
|
||||
switch (item[1])
|
||||
{
|
||||
case CurveTypeEncoding.Arc:
|
||||
curves.Add(Arc.FromList(item));
|
||||
break;
|
||||
case CurveTypeEncoding.Circle:
|
||||
curves.Add(Circle.FromList(item));
|
||||
break;
|
||||
case CurveTypeEncoding.Curve:
|
||||
curves.Add(Curve.FromList(item));
|
||||
break;
|
||||
case CurveTypeEncoding.Ellipse:
|
||||
curves.Add(Ellipse.FromList(item));
|
||||
break;
|
||||
case CurveTypeEncoding.Line:
|
||||
curves.Add(Line.FromList(item));
|
||||
break;
|
||||
case CurveTypeEncoding.Polyline:
|
||||
curves.Add(Polyline.FromList(item));
|
||||
break;
|
||||
case CurveTypeEncoding.PolyCurve:
|
||||
curves.Add(Polycurve.FromList(item));
|
||||
break;
|
||||
}
|
||||
|
||||
currentIndex += itemLength + 1;
|
||||
done = currentIndex >= list.Count;
|
||||
}
|
||||
return curves;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Objects.Primitive;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a sub-curve of a three-dimensional circle.
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Geometry.Arc")]
|
||||
public class Arc : Base, IHasBoundingBox, ICurve, ITransformable<Arc>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the plane of the <see cref="Arc"/>.
|
||||
/// The plane origin is the <see cref="Arc"/> center.
|
||||
/// The plane normal indicates the handedness of the <see cref="Arc"/> such that direction from <see cref="startPoint"/> to <see cref="endPoint"/> is counterclockwise.
|
||||
/// </summary>
|
||||
public required Plane plane { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The start <see cref="Point"/> of the <see cref="Arc"/>
|
||||
/// </summary>
|
||||
public required Point startPoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the point at 0.5 length.
|
||||
/// </summary>
|
||||
public required Point midPoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The end <see cref="Point"/> of the <see cref="Arc"/>
|
||||
/// </summary>
|
||||
public required Point endPoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The radius of the <see cref="Arc"/>
|
||||
/// </summary>
|
||||
public double radius => Point.Distance(plane.origin, startPoint);
|
||||
|
||||
/// <summary>
|
||||
/// OBSOLETE - This is just here for backwards compatibility.
|
||||
/// </summary>
|
||||
[JsonIgnore, Obsolete("start angle should be calculated from arc startpoint and plane if needed", true)]
|
||||
public double? startAngle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// OBSOLETE - This is just here for backwards compatibility.
|
||||
/// </summary>
|
||||
[JsonIgnore, Obsolete("end angle should be calculated from arc endpoint and plane if needed", true)]
|
||||
public double? endAngle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// OBSOLETE - This is just here for backwards compatibility.
|
||||
/// </summary>
|
||||
[JsonIgnore, Obsolete("Refer to measure instead", true)]
|
||||
public double angleRadians { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The measure of the <see cref="Arc"/> in radians.
|
||||
/// Calculated using the arc addition postulate using the <see cref="midPoint"/>.
|
||||
/// </summary>
|
||||
public double measure =>
|
||||
(2 * Math.Asin(Point.Distance(startPoint, midPoint) / (2 * radius)))
|
||||
+ (2 * Math.Asin(Point.Distance(midPoint, endPoint) / (2 * radius)));
|
||||
|
||||
/// <summary>
|
||||
/// The units this object was specified in.
|
||||
/// </summary>
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Interval domain { get; set; } = new() { start = 0, end = 0 };
|
||||
|
||||
/// <summary>
|
||||
/// The length of the <see cref="Arc"/>
|
||||
/// </summary>
|
||||
public double length => radius * measure;
|
||||
|
||||
/// <summary>
|
||||
/// OBSOLETE - This is just here for backwards compatibility.
|
||||
/// </summary>
|
||||
[JsonIgnore, Obsolete("Area property does not belong on an arc", true)]
|
||||
public double area { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box? bbox { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out Arc transformed)
|
||||
{
|
||||
startPoint.TransformTo(transform, out Point transformedStartPoint);
|
||||
midPoint.TransformTo(transform, out Point transformedMidpoint);
|
||||
endPoint.TransformTo(transform, out Point transformedEndPoint);
|
||||
plane.TransformTo(transform, out Plane pln);
|
||||
Arc arc = new()
|
||||
{
|
||||
startPoint = transformedStartPoint,
|
||||
endPoint = transformedEndPoint,
|
||||
midPoint = transformedMidpoint,
|
||||
plane = pln,
|
||||
domain = domain,
|
||||
units = units,
|
||||
};
|
||||
transformed = arc;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out ITransformable transformed)
|
||||
{
|
||||
var res = TransformTo(transform, out Arc arc);
|
||||
transformed = arc;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a flat list with the values of the <see cref="Arc"/>
|
||||
/// This is only used for serialisation purposes.
|
||||
/// </summary>
|
||||
/// <returns>A list of numbers representing the <see cref="Arc"/>'s value</returns>
|
||||
public List<double> ToList()
|
||||
{
|
||||
var list = new List<double>();
|
||||
list.Add(radius);
|
||||
list.Add(0); // Backwards compatibility: start angle
|
||||
list.Add(0); // Backwards compatibility: end angle
|
||||
list.Add(measure);
|
||||
list.Add(domain?.start ?? 0);
|
||||
list.Add(domain?.end ?? 0);
|
||||
list.AddRange(plane.ToList());
|
||||
list.AddRange(startPoint.ToList());
|
||||
list.AddRange(midPoint.ToList());
|
||||
list.AddRange(endPoint.ToList());
|
||||
list.Add(Units.GetEncodingFromUnit(units));
|
||||
list.Insert(0, CurveTypeEncoding.Arc);
|
||||
list.Insert(0, list.Count);
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Arc"/> instance based on a flat list of numerical values.
|
||||
/// This is only used for deserialisation purposes.
|
||||
/// </summary>
|
||||
/// <remarks>The input list should be the result of having called <see cref="Arc.ToList"/></remarks>
|
||||
/// <param name="list">A list of numbers</param>
|
||||
/// <returns>A new <see cref="Arc"/> with the values assigned from the list.</returns>
|
||||
public static Arc FromList(List<double> list)
|
||||
{
|
||||
string units = Units.GetUnitFromEncoding(list[^1]);
|
||||
Arc arc = new()
|
||||
{
|
||||
domain = new Interval { start = list[6], end = list[7] },
|
||||
units = units,
|
||||
plane = Plane.FromList(list.GetRange(8, 13)),
|
||||
startPoint = Point.FromList(list.GetRange(21, 3), units),
|
||||
midPoint = Point.FromList(list.GetRange(24, 3), units),
|
||||
endPoint = Point.FromList(list.GetRange(27, 3), units),
|
||||
};
|
||||
|
||||
arc.plane.units = arc.units;
|
||||
return arc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry.Autocad;
|
||||
|
||||
/// <summary>
|
||||
/// A curve that is comprised of line, arc and/or curve segments, representing the Autocad Polyline, Polyline2d, and Polyline3d classes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="AutocadPolyType.Light"/> and <see cref="AutocadPolyType.Simple2d"/> types will have only <see cref="Line"/>s and <see cref="Arc"/>s in <see cref="Polycurve.segments"/>.
|
||||
/// <see cref="AutocadPolyType.Simple3d"/> type will have only <see cref="Line"/>s in <see cref="Polycurve.segments"/>.
|
||||
/// <see cref="AutocadPolyType.FitCurve2d"/> type will only have <see cref="Arc"/>s in <see cref="Polycurve.segments"/>.
|
||||
/// <see cref="AutocadPolyType.CubicSpline2d"/>, <see cref="AutocadPolyType.CubicSpline3d"/>, <see cref="AutocadPolyType.QuadSpline2d"/>, and <see cref="AutocadPolyType.QuadSpline3d"/> types will have only a single <see cref="Curve"/>s in <see cref="Polycurve.segments"/>.
|
||||
/// </remarks>
|
||||
[SpeckleType("Objects.Geometry.Autocad.AutocadPolycurve")]
|
||||
public class AutocadPolycurve : Polycurve
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the raw coordinates of the vertices.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For <see cref="AutocadPolyType.Light"/> Polylines, these are xy coordinates in the Object Coordinate System (OCS)/>.
|
||||
/// For Polyline2d and Polyline3d types, these are xyz coordinates in the Global Coordinate System. fml.
|
||||
/// </remarks>
|
||||
[DetachProperty, Chunkable(31250)]
|
||||
public required List<double> value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The bulge factor at each vertex. Should be null for Polyline3d.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The bulge factor is used to indicate how much of an arc segment is present at this vertex.
|
||||
/// The bulge factor is the tangent of one fourth the included angle for an arc segment,
|
||||
/// made negative if the arc goes clockwise from the start point to the endpoint.
|
||||
/// A bulge of 0 indicates a straight segment, and a bulge of 1 is a semicircle.
|
||||
/// </remarks>
|
||||
public required List<double>? bulges { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The tangent in radians at each vertex. Should be null for Polyline and Polyline3d.
|
||||
/// </summary>
|
||||
public required List<double?>? tangents { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The normal of the plane of the Autocad Polyline or Polyline2d. Should be null for Polyline3d.
|
||||
/// </summary>
|
||||
public required Vector? normal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The distance from the plane to the origin of the Autocad Polyline or Polyline2d. Should be null for Polyline3d.
|
||||
/// </summary>
|
||||
public double? elevation { get; set; }
|
||||
|
||||
public required AutocadPolyType polyType { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the type of a Autocad Polyline.
|
||||
/// </summary>
|
||||
public enum AutocadPolyType
|
||||
{
|
||||
/// Polyline type is not known
|
||||
Unknown,
|
||||
|
||||
/// Polyline type is the Autocad Polyline class
|
||||
Light,
|
||||
|
||||
Simple2d,
|
||||
|
||||
Simple3d,
|
||||
|
||||
/// The Autocad Polyline2d fit curve poly type. Constructed with pairs of arcs with continuous tangents.
|
||||
FitCurve2d,
|
||||
|
||||
CubicSpline2d,
|
||||
|
||||
CubicSpline3d,
|
||||
|
||||
QuadSpline2d,
|
||||
|
||||
QuadSpline3d,
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Objects.Primitive;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 3-dimensional box oriented on a plane.
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Geometry.Box")]
|
||||
public class Box : Base, IHasVolume, IHasArea, IHasBoundingBox
|
||||
{
|
||||
[JsonIgnore, Obsolete("Use plane property instead", true)]
|
||||
public Plane basePlane
|
||||
{
|
||||
get => plane;
|
||||
set => plane = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the plane that defines the orientation of the <see cref="Box"/>
|
||||
/// </summary>
|
||||
public required Plane plane { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Interval"/> that defines the min and max coordinate in the X direction
|
||||
/// </summary>
|
||||
public required Interval xSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Interval"/> that defines the min and max coordinate in the Y direction
|
||||
/// </summary>
|
||||
public required Interval ySize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Interval"/> that defines the min and max coordinate in the Y direction
|
||||
/// </summary>
|
||||
public required Interval zSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The units this object's coordinates are in.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This should be one of <see cref="Units"/>
|
||||
/// </remarks>
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double area => 2 * (xSize.Length * ySize.Length + xSize.Length * zSize.Length + ySize.Length * zSize.Length);
|
||||
|
||||
[JsonIgnore, Obsolete("Boxs should not have a bounding box", true)]
|
||||
public Box? bbox { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double volume => xSize.Length * ySize.Length * zSize.Length;
|
||||
}
|
||||
@@ -0,0 +1,740 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Objects.Primitive;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a "Boundary Representation" Solid
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Geometry.Brep")]
|
||||
public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable<Brep>, IDisplayValue<List<Mesh>>
|
||||
{
|
||||
/// <summary>
|
||||
/// The unit's this object's coordinates are in.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This should be one of <see cref="Units"/>
|
||||
/// </remarks>
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of surfaces in this <see cref="Brep"/> instance.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public required List<Surface> Surfaces { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the flat list of numbers representing the <see cref="Brep"/>'s surfaces.
|
||||
/// </summary>
|
||||
[DetachProperty, Chunkable(31250)]
|
||||
public List<double> SurfacesValue
|
||||
{
|
||||
get
|
||||
{
|
||||
var list = new List<double>();
|
||||
foreach (var srf in Surfaces)
|
||||
{
|
||||
list.AddRange(srf.ToList());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var list = new List<Surface>();
|
||||
var done = false;
|
||||
var currentIndex = 0;
|
||||
while (!done)
|
||||
{
|
||||
var len = (int)value[currentIndex];
|
||||
list.Add(Surface.FromList(value.GetRange(currentIndex + 1, len)));
|
||||
currentIndex += len + 1;
|
||||
done = currentIndex >= value.Count;
|
||||
}
|
||||
|
||||
Surfaces = list;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of 3-dimensional curves in this <see cref="Brep"/> instance.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public required List<ICurve> Curve3D { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the flat list of numbers representing the <see cref="Brep"/>'s 3D curves.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Curve3D"/> instead.
|
||||
/// </remarks>
|
||||
[DetachProperty, Chunkable(31250)]
|
||||
public List<double> Curve3DValues
|
||||
{
|
||||
get => CurveArrayEncodingExtensions.ToArray(Curve3D);
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
Curve3D = CurveArrayEncodingExtensions.FromArray(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of 2-dimensional UV curves in this <see cref="Brep"/> instance.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public required List<ICurve> Curve2D { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the flat list of numbers representing the <see cref="Brep"/>'s 2D curves.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Curve2D"/> instead.
|
||||
/// </remarks>
|
||||
[DetachProperty, Chunkable(31250)]
|
||||
public List<double> Curve2DValues
|
||||
{
|
||||
get => CurveArrayEncodingExtensions.ToArray(Curve2D);
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
Curve2D = CurveArrayEncodingExtensions.FromArray(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of vertices in this <see cref="Brep"/> instance.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public required List<Point> Vertices { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the flat list of numbers representing the <see cref="Brep"/>'s vertices.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Vertices"/> instead.
|
||||
/// </remarks>
|
||||
[DetachProperty, Chunkable(31250)]
|
||||
public List<double> VerticesValue
|
||||
{
|
||||
get
|
||||
{
|
||||
var list = new List<double>((Vertices.Count * 3) + 1);
|
||||
list.Add(Units.GetEncodingFromUnit(units));
|
||||
foreach (var vertex in Vertices)
|
||||
{
|
||||
list.AddRange(vertex.ToList());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
var units = value.Count % 3 == 0 ? Units.None : Units.GetUnitFromEncoding(value[0]);
|
||||
Vertices = new(value.Count / 3);
|
||||
for (int i = value.Count % 3 == 0 ? 0 : 1; i < value.Count; i += 3)
|
||||
{
|
||||
Vertices.Add(new Point(value[i], value[i + 1], value[i + 2], units));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of edges in this <see cref="Brep"/> instance.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public required List<BrepEdge> Edges { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the flat list of numbers representing the <see cref="Brep"/>'s edges.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Edges"/> instead.
|
||||
/// </remarks>
|
||||
[DetachProperty, Chunkable(62500)]
|
||||
public List<double?> EdgesValue
|
||||
{
|
||||
get =>
|
||||
Edges
|
||||
.SelectMany(e =>
|
||||
{
|
||||
var ints = new List<double?>();
|
||||
ints.Add(e.Curve3dIndex);
|
||||
ints.Add(e.StartIndex);
|
||||
ints.Add(e.EndIndex);
|
||||
ints.Add(Convert.ToInt32(e.ProxyCurveIsReversed));
|
||||
ints.Add(e.Domain.start);
|
||||
ints.Add(e.Domain.end);
|
||||
ints.AddRange(e.TrimIndices.Select(Convert.ToDouble).Cast<double?>());
|
||||
return ints.Prepend(ints.Count);
|
||||
})
|
||||
.ToList();
|
||||
set
|
||||
{
|
||||
Edges = new List<BrepEdge>();
|
||||
if (value == null || value.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
while (i < value.Count)
|
||||
{
|
||||
int n = Convert.ToInt32(value[i]);
|
||||
|
||||
var loopValues = value.GetRange(i + 1, n);
|
||||
var curve3dIndex = Convert.ToInt32(loopValues[0]);
|
||||
var startIndex = Convert.ToInt32(loopValues[1]);
|
||||
var endIndex = Convert.ToInt32(loopValues[2]);
|
||||
var proxyReversed = Convert.ToBoolean(loopValues[3]);
|
||||
var domainStart = loopValues[4];
|
||||
var domainEnd = loopValues[5];
|
||||
Interval domain =
|
||||
domainStart.HasValue && domainEnd.HasValue
|
||||
? new() { start = domainStart.Value, end = domainEnd.Value }
|
||||
: Interval.UnitInterval;
|
||||
|
||||
var trimIndices = loopValues.GetRange(6, loopValues.Count - 6).Select(d => Convert.ToInt32(d)).ToArray();
|
||||
|
||||
var edge = new BrepEdge
|
||||
{
|
||||
Brep = this,
|
||||
Curve3dIndex = curve3dIndex,
|
||||
TrimIndices = trimIndices,
|
||||
StartIndex = startIndex,
|
||||
EndIndex = endIndex,
|
||||
ProxyCurveIsReversed = proxyReversed,
|
||||
Domain = domain,
|
||||
};
|
||||
|
||||
Edges.Add(edge);
|
||||
i += n + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of closed UV loops in this <see cref="Brep"/> instance.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public required List<BrepLoop> Loops { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the flat list of numbers representing the <see cref="Brep"/>'s loops.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Loops"/> instead.
|
||||
/// </remarks>
|
||||
[DetachProperty, Chunkable(62500)]
|
||||
public List<int> LoopsValue
|
||||
{
|
||||
get =>
|
||||
Loops
|
||||
.SelectMany(l =>
|
||||
{
|
||||
var ints = new List<int>();
|
||||
ints.Add(l.FaceIndex);
|
||||
ints.Add((int)l.Type);
|
||||
ints.AddRange(l.TrimIndices);
|
||||
return ints.Prepend(ints.Count);
|
||||
})
|
||||
.ToList();
|
||||
set
|
||||
{
|
||||
Loops = new List<BrepLoop>();
|
||||
if (value == null || value.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
while (i < value.Count)
|
||||
{
|
||||
int n = value[i];
|
||||
|
||||
var loopValues = value.GetRange(i + 1, n);
|
||||
var faceIndex = loopValues[0];
|
||||
var type = (BrepLoopType)loopValues[1];
|
||||
var trimIndices = loopValues.GetRange(2, loopValues.Count - 2);
|
||||
var loop = new BrepLoop
|
||||
{
|
||||
Brep = this,
|
||||
FaceIndex = faceIndex,
|
||||
TrimIndices = trimIndices,
|
||||
Type = type,
|
||||
};
|
||||
Loops.Add(loop);
|
||||
i += n + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of UV trim segments for each surface in this <see cref="Brep"/> instance.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public required List<BrepTrim> Trims { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the flat list of numbers representing the <see cref="Brep"/>'s trims.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Trims"/> instead.
|
||||
/// </remarks>
|
||||
[DetachProperty, Chunkable(62500)]
|
||||
public List<int> TrimsValue
|
||||
{
|
||||
get
|
||||
{
|
||||
List<int> list = new(Trims.Count * TRIMS_ENCODING_LENGTH);
|
||||
foreach (var trim in Trims)
|
||||
{
|
||||
list.Add(trim.EdgeIndex);
|
||||
list.Add(trim.StartIndex);
|
||||
list.Add(trim.EndIndex);
|
||||
list.Add(trim.FaceIndex);
|
||||
list.Add(trim.LoopIndex);
|
||||
list.Add(trim.CurveIndex);
|
||||
list.Add(trim.IsoStatus);
|
||||
list.Add((int)trim.TrimType);
|
||||
list.Add(trim.IsReversed ? 1 : 0);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var list = new List<BrepTrim>(value.Count / TRIMS_ENCODING_LENGTH);
|
||||
for (int i = 0; i < value.Count; i += TRIMS_ENCODING_LENGTH)
|
||||
{
|
||||
var trim = new BrepTrim
|
||||
{
|
||||
Brep = this,
|
||||
EdgeIndex = value[i],
|
||||
StartIndex = value[i + 1],
|
||||
EndIndex = value[i + 2],
|
||||
FaceIndex = value[i + 3],
|
||||
LoopIndex = value[i + 4],
|
||||
CurveIndex = value[i + 5],
|
||||
IsoStatus = value[i + 6],
|
||||
TrimType = (BrepTrimType)value[i + 7],
|
||||
IsReversed = value[i + 8] == 1,
|
||||
Domain = Interval.UnitInterval, //TODO: This is a problem, see CXPLA-28
|
||||
};
|
||||
list.Add(trim);
|
||||
}
|
||||
|
||||
Trims = list;
|
||||
}
|
||||
}
|
||||
|
||||
private const int TRIMS_ENCODING_LENGTH = 9;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of faces in this <see cref="Brep"/> instance.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public required List<BrepFace> Faces { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the flat list of numbers representing the <see cref="Brep"/>'s faces.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Faces"/> instead.
|
||||
/// </remarks>
|
||||
[DetachProperty, Chunkable(62500)]
|
||||
public List<int> FacesValue
|
||||
{
|
||||
get =>
|
||||
Faces
|
||||
.SelectMany(f =>
|
||||
{
|
||||
var ints = new List<int>();
|
||||
ints.Add(f.SurfaceIndex);
|
||||
ints.Add(f.OuterLoopIndex);
|
||||
ints.Add(f.OrientationReversed ? 1 : 0);
|
||||
ints.AddRange(f.LoopIndices);
|
||||
return ints.Prepend(ints.Count);
|
||||
})
|
||||
.ToList();
|
||||
set
|
||||
{
|
||||
if (value == null || value.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Faces = new List<BrepFace>();
|
||||
|
||||
var i = 0;
|
||||
while (i < value.Count)
|
||||
{
|
||||
int n = value[i];
|
||||
|
||||
var faceValues = value.GetRange(i + 1, n);
|
||||
var surfIndex = faceValues[0];
|
||||
var outerLoopIndex = faceValues[1];
|
||||
var orientationIsReversed = faceValues[2] == 1;
|
||||
var loopIndices = faceValues.GetRange(3, faceValues.Count - 3);
|
||||
var face = new BrepFace
|
||||
{
|
||||
Brep = this,
|
||||
SurfaceIndex = surfIndex,
|
||||
LoopIndices = loopIndices,
|
||||
OuterLoopIndex = outerLoopIndex,
|
||||
OrientationReversed = orientationIsReversed,
|
||||
};
|
||||
Faces.Add(face);
|
||||
i += n + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if this <see cref="Brep"/> instance is closed or not.
|
||||
/// </summary>
|
||||
public required bool IsClosed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of surfaces in this <see cref="Brep"/> instance.
|
||||
/// </summary>
|
||||
public required BrepOrientation Orientation { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[DetachProperty]
|
||||
public required List<Mesh> displayValue { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double area { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box? bbox { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double volume { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out Brep transformed)
|
||||
{
|
||||
// transform display values
|
||||
var displayValues = new List<Mesh>(displayValue.Count);
|
||||
foreach (Mesh v in displayValue)
|
||||
{
|
||||
v.TransformTo(transform, out Mesh mesh);
|
||||
displayValues.Add(mesh);
|
||||
}
|
||||
|
||||
// transform surfaces
|
||||
var surfaces = new List<Surface>(Surfaces.Count);
|
||||
foreach (var srf in Surfaces)
|
||||
{
|
||||
srf.TransformTo(transform, out Surface surface);
|
||||
surfaces.Add(surface);
|
||||
}
|
||||
|
||||
// transform curve3d
|
||||
var success3D = true;
|
||||
var transformedCurve3D = new List<ICurve>();
|
||||
foreach (var curve in Curve3D)
|
||||
{
|
||||
if (curve is ITransformable c)
|
||||
{
|
||||
c.TransformTo(transform, out ITransformable tc);
|
||||
transformedCurve3D.Add((ICurve)tc);
|
||||
}
|
||||
else
|
||||
{
|
||||
success3D = false;
|
||||
}
|
||||
}
|
||||
|
||||
// transform vertices
|
||||
var transformedVertices = new List<Point>(Vertices.Count);
|
||||
foreach (var vertex in Vertices)
|
||||
{
|
||||
vertex.TransformTo(transform, out Point transformedVertex);
|
||||
transformedVertices.Add(transformedVertex);
|
||||
}
|
||||
|
||||
transformed = new Brep
|
||||
{
|
||||
units = units,
|
||||
displayValue = displayValues,
|
||||
Surfaces = surfaces,
|
||||
Curve3D = transformedCurve3D,
|
||||
Curve2D = new List<ICurve>(Curve2D),
|
||||
Vertices = transformedVertices,
|
||||
Edges = new List<BrepEdge>(Edges.Count),
|
||||
Loops = new List<BrepLoop>(Loops.Count),
|
||||
Trims = new List<BrepTrim>(Trims.Count),
|
||||
Faces = new List<BrepFace>(Faces.Count),
|
||||
IsClosed = IsClosed,
|
||||
Orientation = Orientation,
|
||||
applicationId = applicationId ?? id,
|
||||
};
|
||||
|
||||
foreach (var e in Edges)
|
||||
{
|
||||
transformed.Edges.Add(
|
||||
new BrepEdge
|
||||
{
|
||||
Brep = transformed,
|
||||
Curve3dIndex = e.Curve3dIndex,
|
||||
TrimIndices = e.TrimIndices,
|
||||
StartIndex = e.StartIndex,
|
||||
EndIndex = e.EndIndex,
|
||||
ProxyCurveIsReversed = e.ProxyCurveIsReversed,
|
||||
Domain = e.Domain,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
foreach (var l in Loops)
|
||||
{
|
||||
transformed.Loops.Add(
|
||||
new BrepLoop
|
||||
{
|
||||
Brep = transformed,
|
||||
FaceIndex = l.FaceIndex,
|
||||
TrimIndices = l.TrimIndices,
|
||||
Type = l.Type,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
foreach (var t in Trims)
|
||||
{
|
||||
transformed.Trims.Add(
|
||||
new BrepTrim
|
||||
{
|
||||
Brep = transformed,
|
||||
EdgeIndex = t.EdgeIndex,
|
||||
FaceIndex = t.FaceIndex,
|
||||
LoopIndex = t.LoopIndex,
|
||||
CurveIndex = t.CurveIndex,
|
||||
IsoStatus = t.IsoStatus,
|
||||
TrimType = t.TrimType,
|
||||
IsReversed = t.IsReversed,
|
||||
StartIndex = t.StartIndex,
|
||||
EndIndex = t.EndIndex,
|
||||
Domain = null!,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
foreach (var f in Faces)
|
||||
{
|
||||
transformed.Faces.Add(
|
||||
new BrepFace
|
||||
{
|
||||
Brep = transformed,
|
||||
SurfaceIndex = f.SurfaceIndex,
|
||||
LoopIndices = f.LoopIndices,
|
||||
OuterLoopIndex = f.OuterLoopIndex,
|
||||
OrientationReversed = f.OrientationReversed,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return success3D;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out ITransformable transformed)
|
||||
{
|
||||
var res = TransformTo(transform, out Brep brep);
|
||||
transformed = brep;
|
||||
return res;
|
||||
}
|
||||
|
||||
[OnDeserialized]
|
||||
internal void OnDeserialized(StreamingContext context)
|
||||
{
|
||||
Surfaces.ForEach(s => s.units = units);
|
||||
|
||||
for (var i = 0; i < Edges.Count; i++)
|
||||
{
|
||||
var e = Edges[i];
|
||||
var existing = e;
|
||||
lock (existing)
|
||||
{
|
||||
if (e.Brep != null)
|
||||
{
|
||||
e = new BrepEdge
|
||||
{
|
||||
Brep = this,
|
||||
Curve3dIndex = e.Curve3dIndex,
|
||||
TrimIndices = e.TrimIndices,
|
||||
StartIndex = e.StartIndex,
|
||||
EndIndex = e.EndIndex,
|
||||
ProxyCurveIsReversed = e.ProxyCurveIsReversed,
|
||||
Domain = e.Domain,
|
||||
};
|
||||
|
||||
Edges[i] = e;
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Brep = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < Loops.Count; i++)
|
||||
{
|
||||
var l = Loops[i];
|
||||
var existingLoop = l;
|
||||
lock (existingLoop)
|
||||
{
|
||||
if (l.Brep != null)
|
||||
{
|
||||
l = new BrepLoop
|
||||
{
|
||||
Brep = this,
|
||||
FaceIndex = l.FaceIndex,
|
||||
TrimIndices = l.TrimIndices,
|
||||
Type = l.Type,
|
||||
};
|
||||
|
||||
Loops[i] = l;
|
||||
}
|
||||
else
|
||||
{
|
||||
l.Brep = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < Trims.Count; i++)
|
||||
{
|
||||
var t = Trims[i];
|
||||
var existingTrim = t;
|
||||
lock (existingTrim)
|
||||
{
|
||||
if (t.Brep != null)
|
||||
{
|
||||
t = new BrepTrim
|
||||
{
|
||||
Brep = this,
|
||||
EdgeIndex = t.EdgeIndex,
|
||||
LoopIndex = t.LoopIndex,
|
||||
CurveIndex = t.CurveIndex,
|
||||
IsoStatus = t.IsoStatus,
|
||||
TrimType = t.TrimType,
|
||||
IsReversed = t.IsReversed,
|
||||
StartIndex = t.StartIndex,
|
||||
EndIndex = t.EndIndex,
|
||||
FaceIndex = t.FaceIndex,
|
||||
Domain = Interval.UnitInterval, //TODO: This is a problem, see CXPLA-28
|
||||
};
|
||||
Trims[i] = t;
|
||||
}
|
||||
else
|
||||
{
|
||||
t.Brep = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < Faces.Count; i++)
|
||||
{
|
||||
var f = Faces[i];
|
||||
var existingFace = f;
|
||||
lock (existingFace)
|
||||
{
|
||||
if (f.Brep != null)
|
||||
{
|
||||
f = new BrepFace
|
||||
{
|
||||
Brep = this,
|
||||
SurfaceIndex = f.SurfaceIndex,
|
||||
LoopIndices = f.LoopIndices,
|
||||
OuterLoopIndex = f.OuterLoopIndex,
|
||||
OrientationReversed = f.OrientationReversed,
|
||||
};
|
||||
Faces[i] = f;
|
||||
}
|
||||
else
|
||||
{
|
||||
f.Brep = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the orientation of a <see cref="Brep"/>
|
||||
/// </summary>
|
||||
public enum BrepOrientation
|
||||
{
|
||||
/// Brep has no specific orientation
|
||||
None = 0,
|
||||
|
||||
/// Brep faces inward
|
||||
Inward = -1,
|
||||
|
||||
/// Brep faces outward
|
||||
Outward = 1,
|
||||
|
||||
/// Orientation is not known
|
||||
Unknown = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the type of a loop in a <see cref="Brep"/>'s face.
|
||||
/// </summary>
|
||||
public enum BrepLoopType
|
||||
{
|
||||
/// Loop type is not known
|
||||
Unknown,
|
||||
|
||||
/// Loop is the outer loop of a face
|
||||
Outer,
|
||||
|
||||
/// Loop is an inner loop of a face
|
||||
Inner,
|
||||
|
||||
/// Loop is a closed curve with no area.
|
||||
Slit,
|
||||
|
||||
/// Loop represents a curve on a surface
|
||||
CurveOnSurface,
|
||||
|
||||
/// Loop is collapsed to a point.
|
||||
PointOnSurface,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the type of a trim in a <see cref="Brep"/>'s loop.
|
||||
/// </summary>
|
||||
public enum BrepTrimType
|
||||
{
|
||||
Unknown,
|
||||
Boundary,
|
||||
Mated,
|
||||
Seam,
|
||||
Singular,
|
||||
CurveOnSurface,
|
||||
PointOnSurface,
|
||||
Slit,
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Objects.Primitive;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an edge of the <see cref="Brep"/>.
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Geometry.BrepEdge")]
|
||||
public class BrepEdge : Base
|
||||
{
|
||||
[JsonIgnore]
|
||||
public required Brep Brep { get; set; }
|
||||
|
||||
public required int Curve3dIndex { get; set; }
|
||||
public required int[] TrimIndices { get; set; }
|
||||
public required int StartIndex { get; set; }
|
||||
public required int EndIndex { get; set; }
|
||||
|
||||
public required bool ProxyCurveIsReversed { get; set; }
|
||||
|
||||
public required Interval Domain { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Point StartVertex => Brep.Vertices[StartIndex];
|
||||
|
||||
[JsonIgnore]
|
||||
public Point EndVertex => Brep.Vertices[EndIndex];
|
||||
|
||||
[JsonIgnore]
|
||||
public IEnumerable<BrepTrim> Trims => TrimIndices.Select(i => Brep.Trims[i]);
|
||||
|
||||
[JsonIgnore]
|
||||
public ICurve Curve => Brep.Curve3D[Curve3dIndex];
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a face on a <see cref="Brep"/>
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Geometry.BrepFace")]
|
||||
public class BrepFace : Base
|
||||
{
|
||||
[JsonIgnore]
|
||||
public required Brep Brep { get; set; }
|
||||
|
||||
public required int SurfaceIndex { get; set; }
|
||||
public required List<int> LoopIndices { get; set; }
|
||||
public required int OuterLoopIndex { get; set; }
|
||||
public required bool OrientationReversed { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public BrepLoop OuterLoop => Brep.Loops[OuterLoopIndex];
|
||||
|
||||
[JsonIgnore]
|
||||
public Surface Surface => Brep.Surfaces[SurfaceIndex];
|
||||
|
||||
[JsonIgnore]
|
||||
public List<BrepLoop> Loops => LoopIndices.Select(i => Brep.Loops[i]).ToList();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a UV Trim Closed Loop on one of the <see cref="Brep"/>'s surfaces.
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Geometry.BrepLoop")]
|
||||
public class BrepLoop : Base
|
||||
{
|
||||
[JsonIgnore]
|
||||
public required Brep Brep { get; set; }
|
||||
|
||||
public required int FaceIndex { get; set; }
|
||||
public required List<int> TrimIndices { get; set; }
|
||||
public required BrepLoopType Type { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public BrepFace Face => Brep.Faces[FaceIndex];
|
||||
|
||||
[JsonIgnore]
|
||||
public List<BrepTrim> Trims => TrimIndices.Select(i => Brep.Trims[i]).ToList();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Objects.Primitive;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a UV Trim curve for one of the <see cref="Brep"/>'s surfaces.
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Geometry.BrepTrim")]
|
||||
public class BrepTrim : Base
|
||||
{
|
||||
[JsonIgnore]
|
||||
public required Brep Brep { get; set; }
|
||||
public required int EdgeIndex { get; set; }
|
||||
public required int StartIndex { get; set; }
|
||||
public required int EndIndex { get; set; }
|
||||
public required int FaceIndex { get; set; }
|
||||
public required int LoopIndex { get; set; }
|
||||
public required int CurveIndex { get; set; }
|
||||
public required int IsoStatus { get; set; }
|
||||
public required BrepTrimType TrimType { get; set; }
|
||||
public required bool IsReversed { get; set; }
|
||||
|
||||
public required Interval Domain { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public BrepFace Face => Brep.Faces[FaceIndex];
|
||||
|
||||
[JsonIgnore]
|
||||
public BrepLoop Loop => Brep.Loops[LoopIndex];
|
||||
|
||||
[JsonIgnore]
|
||||
public BrepEdge? Edge => EdgeIndex != -1 ? Brep.Edges[EdgeIndex] : null;
|
||||
|
||||
[JsonIgnore]
|
||||
public ICurve Curve2d => Brep.Curve2D[CurveIndex];
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
public interface IRawEncodedObject
|
||||
{
|
||||
public RawEncoding encodedValue { get; set; }
|
||||
}
|
||||
|
||||
public abstract class RawEncodedObject : Base, IDisplayValue<List<Mesh>>, IRawEncodedObject, IHasArea, IHasVolume
|
||||
{
|
||||
[DetachProperty]
|
||||
public required List<Mesh> displayValue { get; set; }
|
||||
|
||||
[DetachProperty]
|
||||
public required RawEncoding encodedValue { get; set; }
|
||||
|
||||
public required string units { get; set; }
|
||||
|
||||
public double area { get; set; }
|
||||
|
||||
public double volume { get; set; }
|
||||
}
|
||||
|
||||
[SpeckleType("Objects.Geometry.BrepX")]
|
||||
public class BrepX : RawEncodedObject;
|
||||
|
||||
[SpeckleType("Objects.Geometry.ExtrusionX")]
|
||||
public class ExtrusionX : RawEncodedObject;
|
||||
|
||||
[SpeckleType("Objects.Geometry.SubDX")]
|
||||
public class SubDX : RawEncodedObject;
|
||||
@@ -0,0 +1,82 @@
|
||||
using Speckle.Objects.Primitive;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a circular curve based on a base <see cref="Plane"/> and a <see cref="double"/> as radius.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These circles are expected to be full (untrimmed) circles.
|
||||
/// For trimmed circles, convert them as <see cref="Arc"/>s instead
|
||||
/// </remarks>
|
||||
[SpeckleType("Objects.Geometry.Circle")]
|
||||
public class Circle : Base, ICurve, IHasArea, IHasBoundingBox
|
||||
{
|
||||
/// <summary>
|
||||
/// The radius of the circle
|
||||
/// </summary>
|
||||
public required double radius { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Plane"/> the circle lies in.
|
||||
/// </summary>
|
||||
public required Plane plane { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The units this object was modeled in.
|
||||
/// </summary>
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Interval domain { get; set; } = Interval.UnitInterval;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double length => 2 * Math.PI * radius;
|
||||
|
||||
//public Point center { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double area => Math.PI * radius * radius;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box? bbox { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the coordinates of this <see cref="Circle"/> as a list of numbers
|
||||
/// </summary>
|
||||
/// <returns>A list of values representing the <see cref="Circle"/></returns>
|
||||
public List<double> ToList()
|
||||
{
|
||||
var list = new List<double>();
|
||||
|
||||
list.Add(radius);
|
||||
list.Add(domain.start);
|
||||
list.Add(domain.end);
|
||||
list.AddRange(plane.ToList());
|
||||
|
||||
list.Add(Units.GetEncodingFromUnit(units));
|
||||
list.Insert(0, CurveTypeEncoding.Circle);
|
||||
list.Insert(0, list.Count);
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Circle"/> based on a list of coordinates and the unit they're drawn in.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of values representing this <see cref="Circle"/></param>
|
||||
/// <returns>A new <see cref="Circle"/> with the provided values.</returns>
|
||||
public static Circle FromList(List<double> list)
|
||||
{
|
||||
var circle = new Circle
|
||||
{
|
||||
radius = list[2],
|
||||
domain = new Interval { start = list[3], end = list[4] },
|
||||
plane = Plane.FromList(list.GetRange(5, 13)),
|
||||
units = Units.GetUnitFromEncoding(list[^1]),
|
||||
};
|
||||
|
||||
return circle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
[SpeckleType("Objects.Geometry.ControlPoint")]
|
||||
public class ControlPoint : Point, ITransformable<ControlPoint>
|
||||
{
|
||||
public ControlPoint() { }
|
||||
|
||||
[SetsRequiredMembers]
|
||||
public ControlPoint(double x, double y, double z, double w, string units, string? applicationId = null)
|
||||
: base(x, y, z, units, applicationId)
|
||||
{
|
||||
weight = w;
|
||||
}
|
||||
|
||||
public required double weight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// OBSOLETE - This is just here for backwards compatibility.
|
||||
/// </summary>
|
||||
[
|
||||
JsonProperty(NullValueHandling = NullValueHandling.Ignore),
|
||||
Obsolete("Access coordinates using XYZ and weight fields", true)
|
||||
]
|
||||
private new List<double> value
|
||||
{
|
||||
#pragma warning disable CS8603 // Possible null reference return. Reason: obsolete.
|
||||
get => null;
|
||||
#pragma warning restore CS8603 // Possible null reference return. Reason: obsolete.
|
||||
set
|
||||
{
|
||||
x = value[0];
|
||||
y = value[1];
|
||||
z = value[2];
|
||||
weight = value.Count > 3 ? value[3] : 1;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TransformTo(Transform transform, out ControlPoint transformed)
|
||||
{
|
||||
TransformTo(transform, out Point transformedPoint);
|
||||
transformed = new ControlPoint(
|
||||
transformedPoint.x,
|
||||
transformedPoint.y,
|
||||
transformedPoint.z,
|
||||
weight,
|
||||
units,
|
||||
applicationId
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{{x},{y},{z},{weight}}}";
|
||||
}
|
||||
|
||||
public void Deconstruct(out double x, out double y, out double z, out double weight)
|
||||
{
|
||||
Deconstruct(out x, out y, out z, out weight, out _);
|
||||
}
|
||||
|
||||
public void Deconstruct(out double x, out double y, out double z, out double weight, out string? units)
|
||||
{
|
||||
Deconstruct(out x, out y, out z, out units);
|
||||
weight = this.weight;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Objects.Primitive;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
[SpeckleType("Objects.Geometry.Curve")]
|
||||
public class Curve : Base, ICurve, IHasBoundingBox, IHasArea, ITransformable<Curve>, IDisplayValue<Polyline>
|
||||
{
|
||||
public required int degree { get; set; }
|
||||
|
||||
public required bool periodic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// "True" if weights differ, "False" if weights are the same.
|
||||
/// </summary>
|
||||
public required bool rational { get; set; }
|
||||
|
||||
[DetachProperty, Chunkable(31250)]
|
||||
public required List<double> points { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the weights for this <see cref="Curve"/>. Use a default value of 1 for unweighted points.
|
||||
/// </summary>
|
||||
[DetachProperty, Chunkable(31250)]
|
||||
public required List<double> weights { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the knots for this <see cref="Curve"/>. Count should be equal to <see cref="points"/> count + <see cref="degree"/> + 1.
|
||||
/// </summary>
|
||||
[DetachProperty, Chunkable(31250)]
|
||||
public required List<double> knots { get; set; }
|
||||
|
||||
public required bool closed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The units this object was specified in.
|
||||
/// </summary>
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Interval domain { get; set; } = Interval.UnitInterval;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double length { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[DetachProperty]
|
||||
public required Polyline displayValue { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double area { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box? bbox { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out Curve transformed)
|
||||
{
|
||||
// transform points
|
||||
var transformedPoints = new List<Point>();
|
||||
foreach (var point in GetPoints())
|
||||
{
|
||||
point.TransformTo(transform, out Point transformedPoint);
|
||||
transformedPoints.Add(transformedPoint);
|
||||
}
|
||||
|
||||
var result = displayValue.TransformTo(transform, out ITransformable polyline);
|
||||
transformed = new Curve
|
||||
{
|
||||
degree = degree,
|
||||
periodic = periodic,
|
||||
rational = rational,
|
||||
points = transformedPoints.SelectMany(o => o.ToList()).ToList(),
|
||||
weights = weights,
|
||||
knots = knots,
|
||||
displayValue = (Polyline)polyline,
|
||||
closed = closed,
|
||||
units = units,
|
||||
applicationId = applicationId,
|
||||
domain = domain != null ? new Interval { start = domain.start, end = domain.end } : Interval.UnitInterval,
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out ITransformable transformed)
|
||||
{
|
||||
var res = TransformTo(transform, out Curve curve);
|
||||
transformed = curve;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <returns><see cref="points"/> as list of <see cref="Point"/>s</returns>
|
||||
/// <exception cref="SpeckleException">when list is malformed</exception>
|
||||
public List<Point> GetPoints()
|
||||
{
|
||||
if (points.Count % 3 != 0)
|
||||
{
|
||||
throw new SpeckleException(
|
||||
$"{nameof(Curve)}.{nameof(points)} list is malformed: expected length to be multiple of 3"
|
||||
);
|
||||
}
|
||||
|
||||
var pts = new List<Point>(points.Count / 3);
|
||||
for (int i = 2; i < points.Count; i += 3)
|
||||
{
|
||||
pts.Add(new Point(points[i - 2], points[i - 1], points[i], units));
|
||||
}
|
||||
|
||||
return pts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the values of this <see cref="Curve"/> as a list of numbers.
|
||||
/// </summary>
|
||||
/// <returns>A list of values representing the <see cref="Curve"/></returns>
|
||||
/// <remarks>
|
||||
/// This is currently only used for encoding optimization in curves in breps!
|
||||
/// </remarks>
|
||||
public List<double> ToList()
|
||||
{
|
||||
var list = new List<double>();
|
||||
var curve = this;
|
||||
list.Add(curve.degree); // 0
|
||||
list.Add(curve.periodic ? 1 : 0); // 1
|
||||
list.Add(curve.rational ? 1 : 0); // 2
|
||||
list.Add(curve.closed ? 1 : 0); // 3
|
||||
list.Add(curve.domain?.start ?? 0); // 4
|
||||
list.Add(curve.domain?.end ?? 1); // 5
|
||||
|
||||
list.Add(curve.points.Count); // 6
|
||||
list.Add(curve.weights.Count); // 7
|
||||
list.Add(curve.knots.Count); // 8
|
||||
|
||||
list.AddRange(curve.points); // 9 onwards
|
||||
list.AddRange(curve.weights);
|
||||
list.AddRange(curve.knots);
|
||||
|
||||
list.Add(Units.GetEncodingFromUnit(units));
|
||||
list.Insert(0, CurveTypeEncoding.Curve);
|
||||
list.Insert(0, list.Count);
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Curve"/> based on a list of coordinates and the unit they're drawn in.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of values representing this <see cref="Curve"/></param>
|
||||
/// <returns>A new <see cref="Curve"/> with the provided values.</returns>
|
||||
/// <remarks>
|
||||
/// This is currently being used only for deserialization of Brep curves!
|
||||
/// </remarks>
|
||||
public static Curve FromList(List<double> list)
|
||||
{
|
||||
if ((int)list[0] != list.Count - 1)
|
||||
{
|
||||
throw new ArgumentException($"Incorrect length. Expected {list[0]}, got {list.Count}", nameof(list));
|
||||
}
|
||||
|
||||
if (list[1] != CurveTypeEncoding.Curve)
|
||||
{
|
||||
throw new ArgumentException($"Wrong curve type. Expected {CurveTypeEncoding.Curve}, got {list[1]}", nameof(list));
|
||||
}
|
||||
|
||||
var pointsCount = (int)list[8];
|
||||
var weightsCount = (int)list[9];
|
||||
var knotsCount = (int)list[10];
|
||||
|
||||
string units = Units.GetUnitFromEncoding(list[^1]);
|
||||
var curve = new Curve
|
||||
{
|
||||
degree = (int)list[2],
|
||||
periodic = (int)list[3] == 1,
|
||||
rational = (int)list[4] == 1,
|
||||
closed = (int)list[5] == 1,
|
||||
domain = new Interval { start = list[6], end = list[7] },
|
||||
displayValue = new Polyline { value = new(), units = units }, // this is unique to breps, so we do not create curves with null displayValues
|
||||
points = list.GetRange(11, pointsCount),
|
||||
weights = list.GetRange(11 + pointsCount, weightsCount),
|
||||
knots = list.GetRange(11 + pointsCount + weightsCount, knotsCount),
|
||||
units = units,
|
||||
};
|
||||
|
||||
return curve;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using Speckle.Objects.Primitive;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
[SpeckleType("Objects.Geometry.Ellipse")]
|
||||
public class Ellipse : Base, ICurve, IHasArea
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the first radius of the <see cref="Ellipse"/>. This is usually the major radius.
|
||||
/// </summary>
|
||||
public required double firstRadius { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the second radius of the <see cref="Ellipse"/>. This is usually the minor radius.
|
||||
/// </summary>
|
||||
public required double secondRadius { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the plane to draw this ellipse in.
|
||||
/// </summary>
|
||||
public required Plane plane { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the domain interval to trim this <see cref="Ellipse"/> with.
|
||||
/// </summary>
|
||||
public Interval? trimDomain { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Box? bbox { get; set; }
|
||||
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the domain interval for this <see cref="Ellipse"/>.
|
||||
/// </summary>
|
||||
public required Interval domain { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public double length { get; set; }
|
||||
|
||||
//public Point center { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public double area { get; set; }
|
||||
|
||||
public List<double> ToList()
|
||||
{
|
||||
var list = new List<double>();
|
||||
list.Add(firstRadius);
|
||||
list.Add(secondRadius);
|
||||
list.Add(domain.start);
|
||||
list.Add(domain.end);
|
||||
|
||||
list.AddRange(plane.ToList());
|
||||
|
||||
list.Add(Units.GetEncodingFromUnit(units));
|
||||
list.Insert(0, CurveTypeEncoding.Ellipse);
|
||||
list.Insert(0, list.Count);
|
||||
return list;
|
||||
}
|
||||
|
||||
public static Ellipse FromList(List<double> list)
|
||||
{
|
||||
var ellipse = new Ellipse
|
||||
{
|
||||
firstRadius = list[2],
|
||||
secondRadius = list[3],
|
||||
domain = new Interval { start = list[4], end = list[5] },
|
||||
plane = Plane.FromList(list.GetRange(6, 13)),
|
||||
units = Units.GetUnitFromEncoding(list[^1]),
|
||||
};
|
||||
return ellipse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Objects.Primitive;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
[SpeckleType("Objects.Geometry.Line")]
|
||||
public class Line : Base, ICurve, IHasBoundingBox, ITransformable<Line>
|
||||
{
|
||||
public Line() { }
|
||||
|
||||
/// <param name="coordinates"></param>
|
||||
/// <param name="units"></param>
|
||||
/// <param name="applicationId"></param>
|
||||
/// <exception cref="ArgumentException"><paramref name="coordinates"/> must have a length of 6</exception>
|
||||
[SetsRequiredMembers]
|
||||
public Line(IList<double> coordinates, string units, string? applicationId = null)
|
||||
{
|
||||
if (coordinates.Count < 6)
|
||||
{
|
||||
throw new ArgumentException("Line from coordinate array requires 6 coordinates.", nameof(coordinates));
|
||||
}
|
||||
|
||||
start = new Point(coordinates[0], coordinates[1], coordinates[2], units, applicationId);
|
||||
end = new Point(coordinates[3], coordinates[4], coordinates[5], units, applicationId);
|
||||
this.units = units;
|
||||
this.applicationId = applicationId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OBSOLETE - This is just here for backwards compatibility.
|
||||
/// You should not use this for anything.
|
||||
/// </summary>
|
||||
[JsonIgnore, Obsolete("Area should not be on the line class", true)]
|
||||
public double area => 0;
|
||||
|
||||
public required string units { get; set; }
|
||||
|
||||
public required Point start { get; set; }
|
||||
public required Point end { get; set; }
|
||||
|
||||
public Interval domain { get; set; } = Interval.UnitInterval;
|
||||
public double length => Point.Distance(start, end);
|
||||
|
||||
public Box? bbox { get; set; }
|
||||
|
||||
public bool TransformTo(Transform transform, out Line transformed)
|
||||
{
|
||||
start.TransformTo(transform, out Point transformedStart);
|
||||
end.TransformTo(transform, out Point transformedEnd);
|
||||
transformed = new Line
|
||||
{
|
||||
start = transformedStart,
|
||||
end = transformedEnd,
|
||||
applicationId = applicationId,
|
||||
units = units,
|
||||
domain = new() { start = domain.start, end = domain.end },
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TransformTo(Transform transform, out ITransformable transformed)
|
||||
{
|
||||
var res = TransformTo(transform, out Line line);
|
||||
transformed = line;
|
||||
return res;
|
||||
}
|
||||
|
||||
public List<double> ToList()
|
||||
{
|
||||
var list = new List<double>();
|
||||
list.AddRange(start.ToList());
|
||||
list.AddRange(end.ToList());
|
||||
list.Add(domain?.start ?? 0);
|
||||
list.Add(domain?.end ?? 1);
|
||||
list.Add(Units.GetEncodingFromUnit(units));
|
||||
list.Insert(0, CurveTypeEncoding.Line);
|
||||
list.Insert(0, list.Count);
|
||||
return list;
|
||||
}
|
||||
|
||||
public static Line FromList(IReadOnlyList<double> list)
|
||||
{
|
||||
var units = Units.GetUnitFromEncoding(list[^1]);
|
||||
var startPt = new Point(list[2], list[3], list[4], units);
|
||||
var endPt = new Point(list[5], list[6], list[7], units);
|
||||
var line = new Line
|
||||
{
|
||||
start = startPt,
|
||||
end = endPt,
|
||||
units = units,
|
||||
domain = new Interval { start = list[8], end = list[9] },
|
||||
};
|
||||
return line;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OBSOLETE - This is just here for backwards compatibility.
|
||||
/// You should not use this for anything. Access coordinates using start and end point.
|
||||
/// </summary>
|
||||
[
|
||||
JsonProperty(NullValueHandling = NullValueHandling.Ignore),
|
||||
Obsolete("Access coordinates using start and end point", true)
|
||||
]
|
||||
public List<double>? value
|
||||
{
|
||||
get => null;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
start = new Point(value[0], value[1], value[2], Units.Meters);
|
||||
end = new Point(value[3], value[4], value[5], Units.Meters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
using System.Diagnostics.Contracts;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Objects.Utils;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <remarks><a href="https://speckle.notion.site/Objects-Geometry-Mesh-9b0bf5ab92bf42f58bf2fe3922d2efca">More docs on notion</a></remarks>
|
||||
[SpeckleType("Objects.Geometry.Mesh")]
|
||||
public class Mesh : Base, IHasBoundingBox, IHasVolume, IHasArea, ITransformable<Mesh>
|
||||
{
|
||||
/// <summary>
|
||||
/// Flat list of vertex data (flat <c>x,y,z,x,y,z...</c> list)
|
||||
/// </summary>
|
||||
[DetachProperty, Chunkable(31250)]
|
||||
public required List<double> vertices { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Flat list of face data<br/>
|
||||
/// Each face starts with the length of the face (e.g. 3 in the case of triangles), followed by that many indices
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// N-gons are supported, but large values of n (> ~50) tend to cause significant performance problems for consumers (e.g. HostApps and <see cref="MeshTriangulationHelper"/>.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>[
|
||||
/// 3, 0, 1, 2, //first face, a triangle (3-gon)
|
||||
/// 4, 1, 2, 3, 4, //second face, a quad (4-gon)
|
||||
/// 6, 4, 5, 6, 7, 8, 9, //third face, an n-gon (6-gon)
|
||||
/// ];</code></example>
|
||||
[DetachProperty, Chunkable(62500)]
|
||||
public required List<int> faces { get; set; }
|
||||
|
||||
/// <summary>Vertex colors as ARGB <see cref="int"/>s</summary>
|
||||
/// <remarks>Expected that there are either 1 color per vertex, or an empty <see cref="List{T}"/></remarks>
|
||||
[DetachProperty, Chunkable(62500)]
|
||||
public List<int> colors { get; set; } = new();
|
||||
|
||||
/// <summary>Flat list of texture coordinates (flat <c>u,v,u,v,u,v...</c> list)</summary>
|
||||
/// <remarks>Expected that there are either 1 texture coordinate per vertex, or an empty <see cref="List{T}"/></remarks>
|
||||
[DetachProperty, Chunkable(31250)]
|
||||
public List<double> textureCoordinates { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// <summary>Flat list of vertex normal data (flat <c>x,y,z,x,y,z...</c> list)</summary>
|
||||
/// <remarks>Expected that there are either 1 texture coordinate per vertex, or an empty <see cref="List{T}"/></remarks>
|
||||
/// </summary>
|
||||
[DetachProperty, Chunkable(31250)]
|
||||
public List<double> vertexNormals { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The unit's this <see cref="Mesh"/> is in.
|
||||
/// This should be one of <see cref="Units"/>
|
||||
/// </summary>
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double area { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box? bbox { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double volume { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Transform(Transform transform)
|
||||
{
|
||||
// transform vertices
|
||||
vertices = GetPoints()
|
||||
.SelectMany(vertex =>
|
||||
{
|
||||
vertex.TransformTo(transform, out Point transformedVertex);
|
||||
return transformedVertex.ToList();
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out Mesh transformed)
|
||||
{
|
||||
// transform vertices
|
||||
var transformedVertices = new List<Point>();
|
||||
foreach (var vertex in GetPoints())
|
||||
{
|
||||
vertex.TransformTo(transform, out Point transformedVertex);
|
||||
transformedVertices.Add(transformedVertex);
|
||||
}
|
||||
|
||||
transformed = new Mesh
|
||||
{
|
||||
vertices = transformedVertices.SelectMany(o => o.ToList()).ToList(),
|
||||
textureCoordinates = textureCoordinates,
|
||||
applicationId = applicationId ?? id,
|
||||
faces = faces,
|
||||
colors = colors,
|
||||
units = units,
|
||||
};
|
||||
transformed["renderMaterial"] = this["renderMaterial"];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out ITransformable transformed)
|
||||
{
|
||||
var res = TransformTo(transform, out Mesh brep);
|
||||
transformed = brep;
|
||||
return res;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public int VerticesCount => vertices.Count / 3;
|
||||
|
||||
[JsonIgnore]
|
||||
public int TextureCoordinatesCount => textureCoordinates.Count / 2;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a vertex as a <see cref="Point"/> by <paramref name="index"/>
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the vertex</param>
|
||||
/// <returns>Vertex as a <see cref="Point"/></returns>
|
||||
/// <remarks>It is usually recommended to instead consume the <see cref="vertices"/> list manually for better performance</remarks>
|
||||
[Pure]
|
||||
public Point GetPoint(int index)
|
||||
{
|
||||
index *= 3;
|
||||
return new Point(vertices[index], vertices[index + 1], vertices[index + 2], units, applicationId);
|
||||
}
|
||||
|
||||
/// <returns><see cref="vertices"/> as list of <see cref="Point"/>s</returns>
|
||||
/// <exception cref="SpeckleException">when list is malformed</exception>
|
||||
/// <remarks>It is usually recommended to instead consume the <see cref="vertices"/> list manually for better performance</remarks>
|
||||
[Pure]
|
||||
public List<Point> GetPoints()
|
||||
{
|
||||
if (vertices.Count % 3 != 0)
|
||||
{
|
||||
throw new SpeckleException(
|
||||
$"{nameof(Mesh)}.{nameof(vertices)} list is malformed: expected length to be multiple of 3"
|
||||
);
|
||||
}
|
||||
|
||||
var pts = new List<Point>(vertices.Count / 3);
|
||||
for (int i = 2; i < vertices.Count; i += 3)
|
||||
{
|
||||
pts.Add(new Point(vertices[i - 2], vertices[i - 1], vertices[i], units));
|
||||
}
|
||||
|
||||
return pts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a texture coordinate as a <see cref="ValueTuple{T1, T2}"/> by <paramref name="index"/>
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the texture coordinate</param>
|
||||
/// <returns>Texture coordinate as a <see cref="ValueTuple{T1, T2}"/></returns>
|
||||
[Pure]
|
||||
public (double, double) GetTextureCoordinate(int index)
|
||||
{
|
||||
index *= 2;
|
||||
return (textureCoordinates[index], textureCoordinates[index + 1]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// A 3-dimensional Plane consisting of an origin <see cref="Point"/>, and 3 <see cref="Vector"/> as its X, Y and Z axis.
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Geometry.Plane")]
|
||||
public class Plane : Base, ITransformable<Plane>
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="Plane"/>s origin point.
|
||||
/// </summary>
|
||||
public required Point origin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Plane"/>s Z axis.
|
||||
/// </summary>
|
||||
public required Vector normal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Plane"/>s X axis.
|
||||
/// </summary>
|
||||
public required Vector xdir { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Plane"/>s Y axis.
|
||||
/// </summary>
|
||||
public required Vector ydir { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unit's this <see cref="Plane"/> is in.
|
||||
/// This should be one of <see cref="Units"/>
|
||||
/// </summary>
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out Plane transformed)
|
||||
{
|
||||
origin.TransformTo(transform, out Point transformedOrigin);
|
||||
normal.TransformTo(transform, out Vector transformedNormal);
|
||||
xdir.TransformTo(transform, out Vector transformedXdir);
|
||||
ydir.TransformTo(transform, out Vector transformedYdir);
|
||||
transformed = new Plane
|
||||
{
|
||||
origin = transformedOrigin,
|
||||
normal = transformedNormal,
|
||||
xdir = transformedXdir,
|
||||
ydir = transformedYdir,
|
||||
applicationId = applicationId,
|
||||
units = units,
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out ITransformable transformed)
|
||||
{
|
||||
var res = TransformTo(transform, out Plane plane);
|
||||
transformed = plane;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the values of this <see cref="Plane"/> as a list of numbers
|
||||
/// </summary>
|
||||
/// <returns>A list of values representing the Plane.</returns>
|
||||
public List<double> ToList()
|
||||
{
|
||||
var list = new List<double>();
|
||||
|
||||
list.AddRange(origin.ToList());
|
||||
list.AddRange(normal.ToList());
|
||||
list.AddRange(xdir.ToList());
|
||||
list.AddRange(ydir.ToList());
|
||||
list.Add(Units.GetEncodingFromUnit(units));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Plane"/> based on a list of values and the unit they're drawn in.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of values representing this plane</param>
|
||||
/// <returns>A new <see cref="Plane"/> with the provided values.</returns>
|
||||
public static Plane FromList(IReadOnlyList<double> list)
|
||||
{
|
||||
var units = Units.GetUnitFromEncoding(list[^1]);
|
||||
var plane = new Plane
|
||||
{
|
||||
origin = new Point(list[0], list[1], list[2], units),
|
||||
normal = new Vector(list[3], list[4], list[5], units),
|
||||
xdir = new Vector(list[6], list[7], list[8], units),
|
||||
ydir = new Vector(list[9], list[10], list[11], units),
|
||||
units = units,
|
||||
};
|
||||
|
||||
return plane;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// A 3-dimensional point
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// TODO: The Point class does not override the Equality operator, which means that there may be cases where `Equals` is used instead of `==`, as the comparison will be done by reference, not value.
|
||||
/// </remarks>
|
||||
[SpeckleType("Objects.Geometry.Point")]
|
||||
public class Point : Base, ITransformable<Point>, IEquatable<Point>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public Point() { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Point"/> from a set of coordinates and it's units.
|
||||
/// </summary>
|
||||
/// <param name="x">The x coordinate</param>
|
||||
/// <param name="y">The y coordinate</param>
|
||||
/// <param name="z">The z coordinate</param>
|
||||
/// <param name="units">The units of the point's coordinates. Defaults to Meters. </param>
|
||||
/// <param name="applicationId">The object's unique application ID</param>
|
||||
[SetsRequiredMembers]
|
||||
public Point(double x, double y, double z, string units, string? applicationId = null)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.applicationId = applicationId;
|
||||
this.units = units;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The x coordinate of the point.
|
||||
/// </summary>
|
||||
public required double x { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The y coordinate of the point.
|
||||
/// </summary>
|
||||
public required double y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The z coordinate of the point.
|
||||
/// </summary>
|
||||
public required double z { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The units this <see cref="Point"/> is in.
|
||||
/// This should be one of the units specified in <see cref="Units"/>
|
||||
/// </summary>
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out Point transformed)
|
||||
{
|
||||
var matrix = transform.matrix;
|
||||
|
||||
var unitFactor = Units.GetConversionFactor(transform.units, units); // applied to translation vector
|
||||
var divisor = matrix.M41 + matrix.M42 + matrix.M43 + unitFactor * matrix.M44;
|
||||
var x = (this.x * matrix.M11 + this.y * matrix.M12 + this.z * matrix.M13 + unitFactor * matrix.M14) / divisor;
|
||||
var y = (this.x * matrix.M21 + this.y * matrix.M22 + this.z * matrix.M23 + unitFactor * matrix.M24) / divisor;
|
||||
var z = (this.x * matrix.M31 + this.y * matrix.M32 + this.z * matrix.M33 + unitFactor * matrix.M34) / divisor;
|
||||
|
||||
transformed = new Point(x, y, z, units, applicationId);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out ITransformable transformed)
|
||||
{
|
||||
var res = TransformTo(transform, out Point pt);
|
||||
transformed = pt;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the coordinates of this <see cref="Point"/> as a list of numbers
|
||||
/// </summary>
|
||||
/// <returns>A list of coordinates {x, y, z} </returns>
|
||||
public List<double> ToList()
|
||||
{
|
||||
return new List<double> { x, y, z };
|
||||
}
|
||||
|
||||
public Vector ToVector()
|
||||
{
|
||||
return new Vector(x, y, z, units, applicationId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Point"/> based on a list of coordinates and the unit they're drawn in.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of coordinates {x, y, z}</param>
|
||||
/// <param name="units">The units the coordinates are in</param>
|
||||
/// <returns>A new <see cref="Point"/> with the provided coordinates.</returns>
|
||||
public static Point FromList(IList<double> list, string units)
|
||||
{
|
||||
return new Point(list[0], list[1], list[2], units);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deconstructs a <see cref="Point"/> into it's coordinates and units
|
||||
/// </summary>
|
||||
/// <param name="x">The x coordinate</param>
|
||||
/// <param name="y">The y coordinate</param>
|
||||
/// <param name="z">The z coordinate</param>
|
||||
/// <param name="units">The units the point's coordinates are in.</param>
|
||||
public void Deconstruct(out double x, out double y, out double z, out string? units)
|
||||
{
|
||||
Deconstruct(out x, out y, out z);
|
||||
units = this.units;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deconstructs a <see cref="Point"/> into it's coordinates and units
|
||||
/// </summary>
|
||||
/// <param name="x">The x coordinate</param>
|
||||
/// <param name="y">The y coordinate</param>
|
||||
/// <param name="z">The z coordinate</param>
|
||||
public void Deconstruct(out double x, out double y, out double z)
|
||||
{
|
||||
x = this.x;
|
||||
y = this.y;
|
||||
z = this.z;
|
||||
}
|
||||
|
||||
public static Point operator +(Point point1, Point point2) =>
|
||||
new(point1.x + point2.x, point1.y + point2.y, point1.z + point2.z, point1.units);
|
||||
|
||||
public static Point operator -(Point point1, Point point2) =>
|
||||
new(point1.x - point2.x, point1.y - point2.y, point1.z - point2.z, point1.units);
|
||||
|
||||
public static Point operator *(Point point1, Point point2) =>
|
||||
new(point1.x * point2.x, point1.y * point2.y, point1.z * point2.z, point1.units);
|
||||
|
||||
public static Point operator *(Point point, double val) =>
|
||||
new(point.x * val, point.y * val, point.z * val, point.units);
|
||||
|
||||
public static Point operator /(Point point, double val) =>
|
||||
new(point.x / val, point.y / val, point.z / val, point.units);
|
||||
|
||||
public static bool operator ==(Point? point1, Point? point2)
|
||||
{
|
||||
if (point1 is null && point2 is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (point1 is null || point2 is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return point1.units == point2.units && point1.x == point2.x && point1.y == point2.y && point1.z == point2.z;
|
||||
}
|
||||
|
||||
public static bool operator !=(Point? point1, Point? point2) => !(point1 == point2);
|
||||
|
||||
/// <summary>
|
||||
/// Computes a point equidistant from two points.
|
||||
/// </summary>
|
||||
/// <param name="point1">First point.</param>
|
||||
/// <param name="point2">Second point.</param>
|
||||
/// <returns>A point at the same distance from <paramref name="point1"/> and <paramref name="point2"/></returns>
|
||||
public static Point Midpoint(Point point1, Point point2)
|
||||
{
|
||||
return new Point(
|
||||
0.5 * (point1.x + point2.x),
|
||||
0.5 * (point1.y + point2.y),
|
||||
0.5 * (point1.z + point2.z),
|
||||
point1.units
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the distance between two points
|
||||
/// </summary>
|
||||
/// <param name="point1">First point.</param>
|
||||
/// <param name="point2">Second point.</param>
|
||||
/// <returns>The distance from <paramref name="point1"/> to <paramref name="point2"/></returns>
|
||||
public static double Distance(Point point1, Point point2)
|
||||
{
|
||||
return Math.Sqrt(
|
||||
Math.Pow(point1.x - point2.x, 2) + Math.Pow(point1.y - point2.y, 2) + Math.Pow(point1.z - point2.z, 2)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the distance between two points.
|
||||
/// </summary>
|
||||
/// <param name="point">point for distance measurement</param>
|
||||
/// <returns>The length of the line between this and the other point</returns>
|
||||
public double DistanceTo(Point point)
|
||||
{
|
||||
return Math.Sqrt(Math.Pow(x - point.x, 2) + Math.Pow(y - point.y, 2) + Math.Pow(z - point.z, 2));
|
||||
}
|
||||
|
||||
public bool Equals(Point? other) => this == other;
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(this, obj))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj is Point p)
|
||||
{
|
||||
return this == p;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
#if NETSTANDARD2_0
|
||||
return HashCode.Of(units).And(x).And(y).And(y);
|
||||
#else
|
||||
return HashCode.Combine(units, x, y, z);
|
||||
#endif
|
||||
}
|
||||
|
||||
[Obsolete($"Use {nameof(Vector.ToPoint)}", true)]
|
||||
public Point(Vector _) { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the coordinates of the <see cref="Point"/>
|
||||
/// </summary>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore), Obsolete("Use x,y,z properties instead", true)]
|
||||
public List<double> value
|
||||
{
|
||||
get => null!;
|
||||
set
|
||||
{
|
||||
x = value[0];
|
||||
y = value[1];
|
||||
z = value.Count > 2 ? value[2] : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// A collection of points, with color and size support.
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Geometry.Pointcloud")]
|
||||
public class Pointcloud : Base, IHasBoundingBox, ITransformable<Pointcloud>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the list of points of this <see cref="Pointcloud"/>, stored as a flat list of coordinates [x1,y1,z1,x2,y2,...]
|
||||
/// </summary>
|
||||
[DetachProperty, Chunkable(31250)]
|
||||
public required List<double> points { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of colors of this <see cref="Pointcloud"/>'s points., stored as ARGB <see cref="int"/>s.
|
||||
/// </summary>
|
||||
[DetachProperty, Chunkable(62500)]
|
||||
public List<int> colors { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of sizes of this <see cref="Pointcloud"/>'s points.
|
||||
/// </summary>
|
||||
[DetachProperty, Chunkable(62500)]
|
||||
public List<double> sizes { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The unit's this <see cref="Pointcloud"/> is in.
|
||||
/// This should be one of <see cref="Units"/>
|
||||
/// </summary>
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box? bbox { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out Pointcloud transformed)
|
||||
{
|
||||
// transform points
|
||||
var transformedPoints = new List<Point>();
|
||||
foreach (var point in GetPoints())
|
||||
{
|
||||
point.TransformTo(transform, out Point transformedPoint);
|
||||
transformedPoints.Add(transformedPoint);
|
||||
}
|
||||
|
||||
transformed = new Pointcloud
|
||||
{
|
||||
units = units,
|
||||
points = transformedPoints.SelectMany(o => o.ToList()).ToList(),
|
||||
colors = colors,
|
||||
sizes = sizes,
|
||||
applicationId = applicationId,
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out ITransformable transformed)
|
||||
{
|
||||
var res = TransformTo(transform, out Pointcloud pc);
|
||||
transformed = pc;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <returns><see cref="points"/> as list of <see cref="Point"/>s</returns>
|
||||
/// <exception cref="SpeckleException">when list is malformed</exception>
|
||||
public List<Point> GetPoints()
|
||||
{
|
||||
if (points.Count % 3 != 0)
|
||||
{
|
||||
throw new SpeckleException(
|
||||
$"{nameof(Pointcloud)}.{nameof(points)} list is malformed: expected length to be multiple of 3"
|
||||
);
|
||||
}
|
||||
|
||||
var pts = new List<Point>(points.Count / 3);
|
||||
for (int i = 2; i < points.Count; i += 3)
|
||||
{
|
||||
pts.Add(new Point(points[i - 2], points[i - 1], points[i], units));
|
||||
}
|
||||
|
||||
return pts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Objects.Primitive;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// A curve that is comprised of multiple curves connected.
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Geometry.Polycurve")]
|
||||
public class Polycurve : Base, ICurve, IHasArea, IHasBoundingBox, ITransformable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the list of segments that comprise this <see cref="Polycurve"/>
|
||||
/// </summary>
|
||||
public required List<ICurve> segments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a Boolean value indicating if the <see cref="Polycurve"/> is closed
|
||||
/// (i.e. The start point of the first segment and the end point of the last segment coincide.)
|
||||
/// </summary>
|
||||
public bool closed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unit's this <see cref="Polycurve"/> is in.
|
||||
/// This should be one of <see cref="Units"/>
|
||||
/// </summary>
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The internal domain of this curve.
|
||||
/// </summary>
|
||||
public Interval domain { get; set; } = Interval.UnitInterval;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double length { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double area { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box? bbox { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out ITransformable polycurve)
|
||||
{
|
||||
// transform segments
|
||||
var success = true;
|
||||
var transformed = new List<ICurve>();
|
||||
foreach (var curve in segments)
|
||||
{
|
||||
if (curve is ITransformable c)
|
||||
{
|
||||
c.TransformTo(transform, out ITransformable tc);
|
||||
transformed.Add((ICurve)tc);
|
||||
}
|
||||
else
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
polycurve = new Polycurve
|
||||
{
|
||||
segments = transformed,
|
||||
applicationId = applicationId,
|
||||
closed = closed,
|
||||
units = units,
|
||||
};
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Polycurve"/> instance from an existing <see cref="Polyline"/> curve.
|
||||
/// </summary>
|
||||
/// <param name="polyline">The polyline to be used when constructing the <see cref="Polycurve"/></param>
|
||||
/// <returns>A <see cref="Polycurve"/> with the same shape as the provided polyline.</returns>
|
||||
public static implicit operator Polycurve(Polyline polyline)
|
||||
{
|
||||
Polycurve polycurve = new()
|
||||
{
|
||||
segments = new(),
|
||||
units = polyline.units,
|
||||
area = polyline.area,
|
||||
domain = polyline.domain,
|
||||
closed = polyline.closed,
|
||||
bbox = polyline.bbox,
|
||||
length = polyline.length,
|
||||
};
|
||||
|
||||
var points = polyline.GetPoints();
|
||||
for (var i = 0; i < points.Count - 1; i++)
|
||||
{
|
||||
var line = new Line
|
||||
{
|
||||
start = points[i],
|
||||
end = points[i + 1],
|
||||
units = polyline.units,
|
||||
};
|
||||
polycurve.segments.Add(line);
|
||||
}
|
||||
|
||||
if (polyline.closed)
|
||||
{
|
||||
var line = new Line
|
||||
{
|
||||
start = points[^1],
|
||||
end = points[0],
|
||||
units = polyline.units,
|
||||
};
|
||||
polycurve.segments.Add(line);
|
||||
}
|
||||
|
||||
return polycurve;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the values of this <see cref="Polycurve"/> as a list of numbers
|
||||
/// </summary>
|
||||
/// <returns>A list of values representing the polycurve.</returns>
|
||||
public List<double> ToList()
|
||||
{
|
||||
var list = new List<double>();
|
||||
list.Add(closed ? 1 : 0);
|
||||
list.Add(domain.start);
|
||||
list.Add(domain.end);
|
||||
|
||||
var crvs = CurveArrayEncodingExtensions.ToArray(segments);
|
||||
list.Add(crvs.Count);
|
||||
list.AddRange(crvs);
|
||||
|
||||
list.Add(Units.GetEncodingFromUnit(units));
|
||||
list.Insert(0, CurveTypeEncoding.PolyCurve);
|
||||
list.Insert(0, list.Count);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Polycurve"/> based on a list of coordinates and the unit they're drawn in.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of values representing this polycurve</param>
|
||||
/// <returns>A new <see cref="Polycurve"/> with the provided values.</returns>
|
||||
public static Polycurve FromList(List<double> list)
|
||||
{
|
||||
var temp = list.GetRange(6, (int)list[5]);
|
||||
var polycurve = new Polycurve
|
||||
{
|
||||
segments = CurveArrayEncodingExtensions.FromArray(temp),
|
||||
closed = (int)list[2] == 1,
|
||||
domain = new Interval { start = list[3], end = list[4] },
|
||||
units = Units.GetUnitFromEncoding(list[^1]),
|
||||
};
|
||||
|
||||
return polycurve;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Objects.Primitive;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// A polyline curve, defined by a set of vertices.
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Geometry.Polyline")]
|
||||
public class Polyline : Base, ICurve, IHasArea, IHasBoundingBox, ITransformable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the raw coordinates that define this polyline. Use GetPoints instead to access this data as <see cref="Point"/> instances instead.
|
||||
/// </summary>
|
||||
[DetachProperty, Chunkable(31250)]
|
||||
public required List<double> value { get; set; }
|
||||
|
||||
/// <remarks>
|
||||
/// If true, do not add the last point to the value list. Polyline first and last points should be unique.
|
||||
/// </remarks>
|
||||
public bool closed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unit's this <see cref="Polyline"/> is in.
|
||||
/// This should be one of <see cref="Units"/>
|
||||
/// </summary>
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The internal domain of this curve.
|
||||
/// </summary>
|
||||
public Interval domain { get; set; } = Interval.UnitInterval;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double length { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double area { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box? bbox { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out ITransformable transformed)
|
||||
{
|
||||
// transform points
|
||||
var transformedPoints = new List<Point>();
|
||||
foreach (var point in GetPoints())
|
||||
{
|
||||
point.TransformTo(transform, out Point transformedPoint);
|
||||
transformedPoints.Add(transformedPoint);
|
||||
}
|
||||
|
||||
transformed = new Polyline
|
||||
{
|
||||
value = transformedPoints.SelectMany(o => o.ToList()).ToList(),
|
||||
closed = closed,
|
||||
applicationId = applicationId,
|
||||
units = units,
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///<remarks>This function may be suboptimal for performance for polylines with many points</remarks>
|
||||
/// <returns><see cref="value"/> as List of <see cref="Point"/>s</returns>
|
||||
/// <exception cref="SpeckleException">when list is malformed</exception>
|
||||
public List<Point> GetPoints()
|
||||
{
|
||||
if (value.Count % 3 != 0)
|
||||
{
|
||||
throw new SpeckleException(
|
||||
$"{nameof(Polyline)}.{nameof(value)} list is malformed: expected length to be multiple of 3"
|
||||
);
|
||||
}
|
||||
|
||||
var pts = new List<Point>(value.Count / 3);
|
||||
for (int i = 2; i < value.Count; i += 3)
|
||||
{
|
||||
pts.Add(new Point(value[i - 2], value[i - 1], value[i], units));
|
||||
}
|
||||
|
||||
return pts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the values of this <see cref="Polyline"/> as a list of numbers
|
||||
/// </summary>
|
||||
/// <returns>A list of values representing the polyline.</returns>
|
||||
public List<double> ToList()
|
||||
{
|
||||
var list = new List<double>();
|
||||
list.Add(closed ? 1 : 0); // 2
|
||||
list.Add(domain?.start ?? 0); // 3
|
||||
list.Add(domain?.end ?? 1); // 4
|
||||
list.Add(value.Count); // 5
|
||||
list.AddRange(value); // 6 onwards
|
||||
|
||||
list.Add(Units.GetEncodingFromUnit(units));
|
||||
list.Insert(0, CurveTypeEncoding.Polyline); // 1
|
||||
list.Insert(0, list.Count); // 0
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Polyline"/> based on a list of coordinates and the unit they're drawn in.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of values representing this polyline</param>
|
||||
/// <returns>A new <see cref="Polyline"/> with the provided values.</returns>
|
||||
public static Polyline FromList(List<double> list)
|
||||
{
|
||||
int pointCount = (int)list[5];
|
||||
return new()
|
||||
{
|
||||
closed = (int)list[2] == 1,
|
||||
domain = new Interval { start = list[3], end = list[4] },
|
||||
value = list.GetRange(6, pointCount),
|
||||
units = Units.GetUnitFromEncoding(list[^1]),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
public static class PolylineExtensions
|
||||
{
|
||||
public static IEnumerable<Line> EnumerateAsLines(this Polyline polyline)
|
||||
{
|
||||
List<Point> points = polyline.GetPoints();
|
||||
if (points.Count == 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
Point previousPoint = points[0];
|
||||
for (int i = 1; i < points.Count; i++)
|
||||
{
|
||||
yield return new Line()
|
||||
{
|
||||
start = previousPoint,
|
||||
end = points[i],
|
||||
units = polyline.units,
|
||||
};
|
||||
previousPoint = points[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Flat polygon, defined by an outer boundary and inner loops.
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Geometry.Region")]
|
||||
public class Region : Base, IHasArea, IHasBoundingBox, ITransformable, IDisplayValue<List<Mesh>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Boundary of a region.
|
||||
/// Should be a planar, closed, non-self-intersecting ICurve.
|
||||
/// </summary>
|
||||
public required ICurve boundary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Loops (voids) in the region.
|
||||
/// Each loop should be planar, closed, non-self-intersecting ICurve, located inside the boundary.
|
||||
/// The loops should not intersect or touch each other.
|
||||
/// </summary>
|
||||
public required List<ICurve> innerLoops { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The units of object's coordinates.
|
||||
/// This should be one of <see cref="Units"/>
|
||||
/// </summary>
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indication whether the region is just a geometry (false) or has a hatch pattern (true).
|
||||
/// It's a distinction for receiving in apps that support both Region and Hatch (aka region with hatch pattern)
|
||||
/// </summary>
|
||||
public required bool hasHatchPattern { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double area { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box? bbox { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
[DetachProperty]
|
||||
public List<Mesh> displayValue { get; set; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out ITransformable transformed)
|
||||
{
|
||||
// assign self to the returned object, in case transformation fails
|
||||
transformed = this;
|
||||
|
||||
// transform boundary
|
||||
if (boundary is ITransformable boundaryTransformable)
|
||||
{
|
||||
boundaryTransformable.TransformTo(transform, out ITransformable transformedBoundaryResult);
|
||||
var transformedBoundary = (ICurve)transformedBoundaryResult;
|
||||
|
||||
// transform inner loops
|
||||
var transformedLoops = new List<ICurve>();
|
||||
foreach (var loop in innerLoops)
|
||||
{
|
||||
if (loop is ITransformable loopTransformable)
|
||||
{
|
||||
loopTransformable.TransformTo(transform, out ITransformable transformedLoop);
|
||||
transformedLoops.Add((ICurve)transformedLoop);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// transform display meshes
|
||||
var transformedMeshes = new List<Mesh>();
|
||||
foreach (var mesh in displayValue)
|
||||
{
|
||||
mesh.TransformTo(transform, out ITransformable transformedMesh);
|
||||
transformedMeshes.Add((Mesh)transformedMesh);
|
||||
}
|
||||
|
||||
// if boundary and loops transformations succeeded
|
||||
transformed = new Region
|
||||
{
|
||||
boundary = transformedBoundary,
|
||||
innerLoops = transformedLoops,
|
||||
hasHatchPattern = hasHatchPattern,
|
||||
displayValue = transformedMeshes,
|
||||
units = units,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using Speckle.Objects.Primitive;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
public enum SpiralType
|
||||
{
|
||||
Biquadratic,
|
||||
BiquadraticParabola,
|
||||
Bloss,
|
||||
Clothoid,
|
||||
Cosine,
|
||||
Cubic,
|
||||
CubicParabola,
|
||||
Radioid,
|
||||
Sinusoid,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
[SpeckleType("Objects.Geometry.Spiral")]
|
||||
public class Spiral : Base, ICurve, IHasBoundingBox, IDisplayValue<Polyline>
|
||||
{
|
||||
public required Point startPoint { get; set; }
|
||||
public required Point endPoint { get; set; }
|
||||
public required Plane plane { get; set; } // plane with origin at spiral center
|
||||
public required double turns { get; set; } // total angle of spiral. positive is counterclockwise, negative is clockwise
|
||||
public required Vector pitchAxis { get; set; } = new(0, 0, 0, Units.None);
|
||||
public required double pitch { get; set; }
|
||||
public required SpiralType spiralType { get; set; }
|
||||
|
||||
public required string units { get; set; }
|
||||
|
||||
public required double length { get; set; }
|
||||
|
||||
public required Interval domain { get; set; }
|
||||
|
||||
[DetachProperty]
|
||||
public required Polyline displayValue { get; set; }
|
||||
|
||||
public Box? bbox { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Objects.Primitive;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// A Surface in NURBS form.
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Geometry.Surface")]
|
||||
public class Surface : Base, IHasBoundingBox, IHasArea, ITransformable<Surface>
|
||||
{
|
||||
[Obsolete("Constructor should only be used by serializer, use one of the other constructors instead")]
|
||||
public Surface()
|
||||
{
|
||||
pointData = [];
|
||||
}
|
||||
|
||||
public Surface(List<List<ControlPoint>> controlPoints)
|
||||
{
|
||||
SetControlPoints(controlPoints);
|
||||
}
|
||||
|
||||
public Surface(IList<double> pointData, int countU, int countV)
|
||||
{
|
||||
this.pointData = pointData;
|
||||
this.countU = countU;
|
||||
this.countV = countV;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The degree of the surface in the U direction
|
||||
/// </summary>
|
||||
public required int degreeU { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The degree of the surface in the V direction
|
||||
/// </summary>
|
||||
public required int degreeV { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the <see cref="Surface"/> is rational.
|
||||
/// </summary>
|
||||
public required bool rational { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The raw data of the surface's control points. Use <see cref="GetControlPoints"/> or <see cref="SetControlPoints"/> instead of accessing this directly.
|
||||
/// </summary>
|
||||
public IList<double> pointData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of control points in the U direction
|
||||
/// </summary>
|
||||
public int countU { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of control points in the V direction
|
||||
/// </summary>
|
||||
public int countV { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The knot vector in the U direction
|
||||
/// </summary>
|
||||
public required List<double> knotsU { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The knot vector in the V direction
|
||||
/// </summary>
|
||||
public required List<double> knotsV { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The surface's domain in the U direction
|
||||
/// </summary>
|
||||
public required Interval domainU { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The surface's domain in the V direction
|
||||
/// </summary>
|
||||
public required Interval domainV { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a surface is closed around the <see cref="domainU"/>.
|
||||
/// </summary>
|
||||
public required bool closedU { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a surface is closed around the <see cref="domainV"/>
|
||||
/// </summary>
|
||||
public required bool closedV { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unit's this <see cref="Surface"/> is in.
|
||||
/// This should be one of <see cref="Units"/>
|
||||
/// </summary>
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double area { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box? bbox { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out Surface transformed)
|
||||
{
|
||||
var ptMatrix = GetControlPoints();
|
||||
foreach (var ctrlPts in ptMatrix)
|
||||
{
|
||||
for (int i = 0; i < ctrlPts.Count; i++)
|
||||
{
|
||||
ctrlPts[i].TransformTo(transform, out var tPt);
|
||||
ctrlPts[i] = tPt;
|
||||
}
|
||||
}
|
||||
|
||||
transformed = new Surface(ptMatrix)
|
||||
{
|
||||
degreeU = degreeU,
|
||||
degreeV = degreeV,
|
||||
countU = countU,
|
||||
countV = countV,
|
||||
rational = rational,
|
||||
closedU = closedU,
|
||||
closedV = closedV,
|
||||
domainU = domainU,
|
||||
domainV = domainV,
|
||||
knotsU = knotsU,
|
||||
knotsV = knotsV,
|
||||
units = units,
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out ITransformable transformed)
|
||||
{
|
||||
var res = TransformTo(transform, out Surface surface);
|
||||
transformed = surface;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control points of this s<see cref="Surface"/>
|
||||
/// </summary>
|
||||
/// <returns>A 2-dimensional array representing this <see cref="Surface"/>s control points.</returns>
|
||||
/// <remarks>The ControlPoints will be ordered following directions "[u][v]"</remarks>
|
||||
public List<List<ControlPoint>> GetControlPoints()
|
||||
{
|
||||
var matrix = new List<List<ControlPoint>>();
|
||||
for (var i = 0; i < countU; i++)
|
||||
{
|
||||
matrix.Add(new List<ControlPoint>());
|
||||
}
|
||||
|
||||
for (var i = 0; i < pointData.Count; i += 4)
|
||||
{
|
||||
var uIndex = i / (countV * 4);
|
||||
matrix[uIndex].Add(new ControlPoint(pointData[i], pointData[i + 1], pointData[i + 2], pointData[i + 3], units));
|
||||
}
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the control points of this <see cref="Surface"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">A 2-dimensional array of <see cref="ControlPoint"/> instances.</param>
|
||||
/// <remarks>The <paramref name="value"/> must be ordered following directions "[u][v]"</remarks>
|
||||
[MemberNotNull(nameof(pointData))]
|
||||
[MemberNotNull(nameof(countU))]
|
||||
[MemberNotNull(nameof(countV))]
|
||||
public void SetControlPoints(List<List<ControlPoint>> value)
|
||||
{
|
||||
List<double> data = new();
|
||||
countU = value.Count;
|
||||
countV = value[0].Count;
|
||||
value.ForEach(row =>
|
||||
row.ForEach(pt =>
|
||||
{
|
||||
data.Add(pt.x);
|
||||
data.Add(pt.y);
|
||||
data.Add(pt.z);
|
||||
data.Add(pt.weight);
|
||||
})
|
||||
);
|
||||
pointData = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the coordinates of this <see cref="Surface"/> as a list of numbers
|
||||
/// </summary>
|
||||
/// <returns>A list of values representing the surface</returns>
|
||||
public List<double> ToList()
|
||||
{
|
||||
var list = new List<double>();
|
||||
list.Add(degreeU);
|
||||
list.Add(degreeV);
|
||||
list.Add(countU);
|
||||
list.Add(countV);
|
||||
list.Add(rational ? 1 : 0);
|
||||
list.Add(closedU ? 1 : 0);
|
||||
list.Add(closedV ? 1 : 0);
|
||||
list.Add(domainU.start); // 7
|
||||
list.Add(domainU.end);
|
||||
list.Add(domainV.start);
|
||||
list.Add(domainV.end); // [0] 10
|
||||
|
||||
list.Add(pointData.Count); // 11
|
||||
list.Add(knotsU.Count); // 12
|
||||
list.Add(knotsV.Count); // 13
|
||||
|
||||
list.AddRange(pointData);
|
||||
list.AddRange(knotsU);
|
||||
list.AddRange(knotsV);
|
||||
|
||||
list.Add(Units.GetEncodingFromUnit(units));
|
||||
list.Insert(0, list.Count);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Surface"/> based on a list of coordinates and the unit they're drawn in.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of values representing this surface</param>
|
||||
/// <returns>A new <see cref="Surface"/> with the provided values.</returns>
|
||||
public static Surface FromList(List<double> list)
|
||||
{
|
||||
var pointCount = (int)list[11];
|
||||
var knotsUCount = (int)list[12];
|
||||
var knotsVCount = (int)list[13];
|
||||
var countU = (int)list[2];
|
||||
var countV = (int)list[3];
|
||||
|
||||
var pointData = list.GetRange(14, pointCount);
|
||||
var u = list[^1];
|
||||
|
||||
return new Surface(pointData, countU, countV)
|
||||
{
|
||||
degreeU = (int)list[0],
|
||||
degreeV = (int)list[1],
|
||||
rational = list[4] == 1,
|
||||
closedU = list[5] == 1,
|
||||
closedV = list[6] == 1,
|
||||
domainU = new Interval { start = list[7], end = list[8] },
|
||||
domainV = new Interval { start = list[9], end = list[10] },
|
||||
knotsU = list.GetRange(14 + pointCount, knotsUCount),
|
||||
knotsV = list.GetRange(14 + pointCount + knotsUCount, knotsVCount),
|
||||
units = Units.GetUnitFromEncoding(u),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// A 3-dimensional vector
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Geometry.Vector")]
|
||||
public class Vector : Base, IHasBoundingBox, ITransformable<Vector>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public Vector() { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new 2D <see cref="Vector"/> from it's X and Y coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">The x coordinate of the vector</param>
|
||||
/// <param name="y">The y coordinate of the vector</param>
|
||||
/// <param name="units">The units the coordinates are in.</param>
|
||||
/// <param name="applicationId">The unique application ID of the object.</param>
|
||||
[SetsRequiredMembers]
|
||||
public Vector(double x, double y, double z, string units, string? applicationId = null)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.applicationId = applicationId;
|
||||
this.units = units;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The unit's this <see cref="Vector"/> is in.
|
||||
/// This should be one of <see cref="Units"/>
|
||||
/// </summary>
|
||||
public required string units { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The x coordinate of the vector.
|
||||
/// </summary>
|
||||
public required double x { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The y coordinate of the vector.
|
||||
/// </summary>
|
||||
public required double y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The z coordinate of the vector.
|
||||
/// </summary>
|
||||
public required double z { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Euclidean length of this vector.
|
||||
/// </summary>
|
||||
/// <returns>Length of the vector.</returns>
|
||||
[JsonIgnore]
|
||||
public double Length => Math.Sqrt(DotProduct(this, this));
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box? bbox { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out Vector transformed)
|
||||
{
|
||||
var m = transform.matrix;
|
||||
var tX = x * m.M11 + y * m.M12 + z * m.M13;
|
||||
var tY = x * m.M21 + y * m.M22 + z * m.M23;
|
||||
var tZ = x * m.M31 + y * m.M32 + z * m.M33;
|
||||
transformed = new Vector(tX, tY, tZ, units, applicationId);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TransformTo(Transform transform, out ITransformable transformed)
|
||||
{
|
||||
_ = TransformTo(transform, out Vector vec);
|
||||
transformed = vec;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the coordinates of this <see cref="Vector"/> as a list of numbers
|
||||
/// </summary>
|
||||
/// <returns>A list of coordinates {x, y, z} </returns>
|
||||
public List<double> ToList() => [x, y, z];
|
||||
|
||||
public Point ToPoint() => new(x, y, z, units, applicationId);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new vector based on a list of coordinates and the unit they're drawn in.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of coordinates {x, y, z}</param>
|
||||
/// <param name="units">The units the coordinates are in</param>
|
||||
/// <returns>A new <see cref="Vector"/> with the provided coordinates.</returns>
|
||||
public static Vector FromList(IReadOnlyList<double> list, string units)
|
||||
{
|
||||
return new Vector(list[0], list[1], list[2], units);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides a vector by a numerical value. This will divide each coordinate by the provided value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The vector to divide</param>
|
||||
/// <param name="val">The value to divide by</param>
|
||||
/// <returns>The resulting <see cref="Vector"/></returns>
|
||||
public static Vector operator /(Vector vector, double val) =>
|
||||
new(vector.x / val, vector.y / val, vector.z / val, vector.units);
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a vector by a numerical value. This will multiply each coordinate by the provided value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The vector to multiply</param>
|
||||
/// <param name="val">The value to multiply by</param>
|
||||
/// <returns>The resulting <see cref="Vector"/></returns>
|
||||
public static Vector operator *(Vector vector, double val) =>
|
||||
new(vector.x * val, vector.y * val, vector.z * val, vector.units);
|
||||
|
||||
/// <summary>
|
||||
/// Adds two vectors by adding each of their coordinates.
|
||||
/// </summary>
|
||||
/// <param name="vector1">The first vector</param>
|
||||
/// <param name="vector2">The second vector</param>
|
||||
/// <returns>The resulting <see cref="Vector"/></returns>
|
||||
public static Vector operator +(Vector vector1, Vector vector2) =>
|
||||
new(vector1.x + vector2.x, vector1.y + vector2.y, vector1.z + vector2.z, vector1.units);
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors by subtracting each of their coordinates.
|
||||
/// </summary>
|
||||
/// <param name="vector1">The first vector</param>
|
||||
/// <param name="vector2">The second vector</param>
|
||||
/// <returns>The resulting <see cref="Vector"/></returns>
|
||||
public static Vector operator -(Vector vector1, Vector vector2) =>
|
||||
new(vector1.x - vector2.x, vector1.y - vector2.y, vector1.z - vector2.z, vector1.units);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scalar product (dot product) of two given vectors
|
||||
/// Dot product = u1*v1 + u2*v2 + u3*v3.
|
||||
/// </summary>
|
||||
/// <param name="u">First vector.</param>
|
||||
/// <param name="v">Second vector.</param>
|
||||
/// <returns>Numerical value of the dot product.</returns>
|
||||
public static double DotProduct(Vector u, Vector v)
|
||||
{
|
||||
return u.x * v.x + u.y * v.y + u.z * v.z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the vector product (cross product) of two given vectors
|
||||
/// Cross product = { u2 * v3 - u3 * v2; u3 * v1 - u1 * v3; u1 * v2 - u2 * v1 }.
|
||||
/// </summary>
|
||||
/// <param name="u">First vector.</param>
|
||||
/// <param name="v">Second vector.</param>
|
||||
/// <returns>Vector result of the cross product.</returns>
|
||||
public static Vector CrossProduct(Vector u, Vector v)
|
||||
{
|
||||
if (u.units != v.units && u.units != Units.None && v.units != Units.None)
|
||||
{
|
||||
throw new ArgumentException("Cannot perform cross product on two vectors with different unit systems");
|
||||
}
|
||||
|
||||
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 Vector(x, y, z, units: u.units);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute and return a unit vector from this vector
|
||||
/// </summary>
|
||||
/// <returns>a normalized unit vector</returns>
|
||||
public void Normalize()
|
||||
{
|
||||
var length = Length;
|
||||
x /= length;
|
||||
y /= length;
|
||||
z /= length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inverses the direction of the vector, equivalent to multiplying by -1
|
||||
/// </summary>
|
||||
/// <returns>A pointing in the opposite direction</returns>
|
||||
public Vector Negate()
|
||||
{
|
||||
x *= -1;
|
||||
y *= -1;
|
||||
z *= -1;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the coordinates of the vector
|
||||
/// </summary>
|
||||
[
|
||||
JsonProperty(NullValueHandling = NullValueHandling.Ignore),
|
||||
Obsolete("Use X,Y,Z fields to access coordinates instead", true)
|
||||
]
|
||||
public List<double> value
|
||||
{
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
get => null;
|
||||
#pragma warning restore CS8603 // Possible null reference return.
|
||||
set
|
||||
{
|
||||
x = value[0];
|
||||
y = value[1];
|
||||
z = value.Count > 2 ? value[2] : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Objects.Primitive;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects;
|
||||
|
||||
#region Generic interfaces.
|
||||
|
||||
/// <summary>
|
||||
/// Represents an object that has a <see cref="IHasBoundingBox.bbox"/>
|
||||
/// </summary>
|
||||
public interface IHasBoundingBox : ISpeckleObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The bounding box containing the object.
|
||||
/// </summary>
|
||||
Box? bbox { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a <see cref="Base"/> object that has <see cref="IHasArea.area"/>
|
||||
/// </summary>
|
||||
public interface IHasArea : ISpeckleObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The area of the object
|
||||
/// </summary>
|
||||
double area { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an object that has <see cref="IHasVolume.volume"/>
|
||||
/// </summary>
|
||||
public interface IHasVolume : ISpeckleObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The volume of the object
|
||||
/// </summary>
|
||||
double volume { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents
|
||||
/// </summary>
|
||||
public interface ICurve : ISpeckleObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The length of the curve.
|
||||
/// </summary>
|
||||
double length { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The numerical domain driving the curve's internal parametrization.
|
||||
/// </summary>
|
||||
Interval domain { get; }
|
||||
|
||||
string units { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic Interface for transformable objects.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object to support transformations.</typeparam>
|
||||
public interface ITransformable<T> : ITransformable
|
||||
where T : ITransformable<T>
|
||||
{
|
||||
/// <inheritdoc cref="ITransformable.TransformTo"/>
|
||||
bool TransformTo(Transform transform, out T transformed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for transformable objects where the type may not be known on convert (eg ICurve implementations)
|
||||
/// </summary>
|
||||
public interface ITransformable : ISpeckleObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a copy of the object with it's coordinates transformed by the provided <paramref name="transform"/>
|
||||
/// </summary>
|
||||
/// <param name="transform">The <see cref="Transform"/> to be applied.</param>
|
||||
/// <param name="transformed">The transformed copy of the object.</param>
|
||||
/// <returns>True if the transform operation was successful, false otherwise.</returns>
|
||||
bool TransformTo(Transform transform, out ITransformable transformed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies displayable <see cref="Base"/> simple geometries to be used as a fallback
|
||||
/// if a displayable form cannot be converted.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <see cref="Base"/> objects that represent conceptual / abstract / mathematically derived geometry
|
||||
/// can use <see cref="displayValue"/> to be used in case the object lacks a natively displayable form.
|
||||
/// (e.g <see cref="Spiral"/>)
|
||||
/// </example>
|
||||
/// <typeparam name="T">
|
||||
/// Type of display value.
|
||||
/// Expected to be either a <see cref="Base"/> type or a <see cref="List{T}"/> of <see cref="Base"/>s,
|
||||
/// Should be constrained to types of <see cref="Point"/>, <see cref="Line"/>, <see cref="Mesh"/> or <see cref="Polyline"/>.
|
||||
/// </typeparam>
|
||||
public interface IDisplayValue<out T> : ISpeckleObject
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="displayValue"/> <see cref="Base"/>(s) will be used to display this <see cref="Base"/>
|
||||
/// if a native displayable object cannot be converted.
|
||||
/// </summary>
|
||||
T displayValue { get; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data objects
|
||||
|
||||
/// <summary>
|
||||
/// Specifies properties on objects to be used for data-based workflows
|
||||
/// </summary>
|
||||
public interface IProperties : ISpeckleObject
|
||||
{
|
||||
Dictionary<string, object?> properties { get; }
|
||||
}
|
||||
|
||||
public interface IDataObject : IProperties, IDisplayValue<IReadOnlyList<Base>>
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the object, primarily used to decorate the object for consumption in frontend and other apps
|
||||
/// </summary>
|
||||
string name { get; }
|
||||
}
|
||||
|
||||
public interface IRevitObject : IDataObject
|
||||
{
|
||||
string type { get; }
|
||||
|
||||
string family { get; }
|
||||
|
||||
string category { get; }
|
||||
|
||||
Base? location { get; }
|
||||
|
||||
IReadOnlyList<IRevitObject> elements { get; }
|
||||
}
|
||||
|
||||
public interface ICivilObject : IDataObject
|
||||
{
|
||||
string type { get; }
|
||||
|
||||
List<ICurve>? baseCurves { get; }
|
||||
|
||||
IReadOnlyList<Base> elements { get; }
|
||||
}
|
||||
|
||||
public interface ITeklaObject : IDataObject
|
||||
{
|
||||
string type { get; }
|
||||
|
||||
IReadOnlyList<ITeklaObject> elements { get; }
|
||||
}
|
||||
|
||||
public interface ICsiObject : IDataObject
|
||||
{
|
||||
string type { get; }
|
||||
|
||||
IReadOnlyList<ICsiObject> elements { get; }
|
||||
}
|
||||
|
||||
public interface IGisObject : IDataObject
|
||||
{
|
||||
string type { get; }
|
||||
}
|
||||
|
||||
public interface IArchicadObject : IDataObject
|
||||
{
|
||||
string type { get; }
|
||||
|
||||
string level { get; }
|
||||
|
||||
IReadOnlyList<IArchicadObject> elements { get; }
|
||||
}
|
||||
|
||||
public interface INavisworksObject : IDataObject { }
|
||||
|
||||
|
||||
#endregion
|
||||
@@ -0,0 +1,20 @@
|
||||
using Speckle.Objects.Data;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Proxies;
|
||||
|
||||
namespace Speckle.Objects.Other;
|
||||
|
||||
/// <summary>
|
||||
/// Proxy for levels as DataObject value.
|
||||
/// <remarks> These proxy lives in Objects library because it depends on DataObject</remarks>
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Other.LevelProxy")]
|
||||
public class LevelProxy : Base, IProxyCollection
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of application ids of objects that use this level
|
||||
/// </summary>
|
||||
public required List<string> objects { get; set; }
|
||||
|
||||
public required DataObject value { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Other;
|
||||
|
||||
/// <summary>
|
||||
/// Keeps track of a raw-encoded object in a native supported format. see <see cref="RawEncodingFormats"/>
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Other.RawEncoding")]
|
||||
public class RawEncoding : Base // note: at this stage, since we're using this for extrusions and subds the name doesn't make sense anymore
|
||||
{
|
||||
public required string format { get; set; }
|
||||
public required string contents { get; set; }
|
||||
|
||||
public RawEncoding() { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Supported encoding types "strongly" typed strings. This needs to match the extension of the file format.
|
||||
/// </summary>
|
||||
public static class RawEncodingFormats
|
||||
{
|
||||
public const string RHINO_3DM = "3dm";
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Drawing;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Objects.Other;
|
||||
|
||||
/// <summary>
|
||||
/// Minimal physically based material DTO class. Based on references from
|
||||
/// https://threejs.org/docs/index.html#api/en/materials/MeshStandardMaterial
|
||||
/// Theoretically has equivalents in Unity and Unreal.
|
||||
///
|
||||
/// See: https://docs.unrealengine.com/en-US/RenderingAndGraphics/Materials/PhysicallyBased/index.html
|
||||
/// And: https://blogs.unity3d.com/2014/10/29/physically-based-shading-in-unity-5-a-primer/
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Other.RenderMaterial")]
|
||||
public class RenderMaterial : Base
|
||||
{
|
||||
public required string name { get; set; }
|
||||
public double opacity { get; set; } = 1;
|
||||
public double metalness { get; set; }
|
||||
public double roughness { get; set; } = 1;
|
||||
|
||||
public required int diffuse { get; set; } = Color.LightGray.ToArgb();
|
||||
|
||||
public int emissive { get; set; } = Color.Black.ToArgb();
|
||||
|
||||
[JsonIgnore]
|
||||
public Color diffuseColor
|
||||
{
|
||||
get => Color.FromArgb(diffuse);
|
||||
set => diffuse = value.ToArgb();
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public Color emissiveColor
|
||||
{
|
||||
get => Color.FromArgb(emissive);
|
||||
set => diffuse = value.ToArgb();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Proxies;
|
||||
|
||||
namespace Speckle.Objects.Other;
|
||||
|
||||
/// <summary>
|
||||
/// Used to store render material to object relationships in root collections
|
||||
/// <remarks> These proxy lives in Objects library because it depends on RenderMaterial</remarks>
|
||||
/// </summary>
|
||||
[SpeckleType("Objects.Other.RenderMaterialProxy")]
|
||||
public class RenderMaterialProxy : Base, IProxyCollection
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of application ids of objects that use this render material
|
||||
/// </summary>
|
||||
public required List<string> objects { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The render material used by <see cref="objects"/>
|
||||
/// </summary>
|
||||
public required RenderMaterial value { get; set; }
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user