Compare commits

..

396 Commits

Author SHA1 Message Date
Adam Hathcock 9794195e9c Merge pull request #293 from specklesystems/main-dev
.NET Build and Publish / build (push) Has been cancelled
Main to dev (NO SQUASH)
2025-05-06 14:00:54 +01:00
Adam Hathcock a61c442930 Merge branch 'dev' into main-dev 2025-05-06 13:50:16 +01:00
Jedd Morgan 68a407905d Add workspaces queries (#291)
* Add workspaces queries

* Format

* extra tweaks

* init speckle verify

* Add workspace creation state

* Add workspace creation test

* test exceptional cases

* GetActiveWorkspace tests

* fixed test
2025-05-01 21:23:30 +03:00
Adam Hathcock 0f2abaf532 Merge remote-tracking branch 'origin/dev' into main-dev 2025-04-30 16:24:06 +01:00
Adam Hathcock 07634b6f6a Merge remote-tracking branch 'origin/dev' into main-dev 2025-04-30 16:22:39 +01:00
Jedd Morgan e938725d35 Added extra test for GetMembers (#290)
* Added extra test for GetMembers

* fixed tests

* verify

* Format

* Run the module init on unit tests and make it json

* update deps

* gitignore support was disabled in csharpier 1.0.1

---------

Co-authored-by: Adam Hathcock <adam@hathcock.uk>
2025-04-30 16:13:41 +01:00
Adam Hathcock d3369e3ce5 Merge pull request #289 from specklesystems/main-dev
Main to dev (don't squash)
2025-04-30 16:00:46 +01:00
KatKatKateryna d75a61d775 Add text class (#271)
.NET Build and Publish / build (push) Has been cancelled
* draft class

* corrections

* edits

* max width

* remove import

* typo

* naming

* move directories

* delete from old location

* comment

* formatting

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-04-30 10:56:29 +02:00
Adam Hathcock 2ae4003afb Merge branch 'main' into main-dev 2025-04-28 10:52:58 +01:00
Adam Hathcock 24db4c4ae4 Merge pull request #288 from specklesystems/adam/no-drop-writes
.NET Build and Publish / build (push) Has been cancelled
fix (main) Don't drop items to write when sending fast
2025-04-28 10:18:48 +01:00
Adam Hathcock edf63d4a1b fix build issue 2025-04-28 09:39:46 +01:00
Adam Hathcock b5b0922e7f Revert to write async 2025-04-28 09:35:02 +01:00
Adam Hathcock ff390f772d just wait for space instead of another task and reduce size to 1000 2025-04-25 18:24:34 +01:00
Adam Hathcock d69f0bba2a fmt 2025-04-25 18:13:05 +01:00
Adam Hathcock 33c14fc14c Remove extras 2025-04-25 18:09:04 +01:00
Adam Hathcock 536e58aacc Don't drop items to write when sending fast 2025-04-25 17:45:01 +01:00
Adam Hathcock 88188aace6 Merge pull request #287 from specklesystems/main-dev
Main to dev (NO SQUASH)
2025-04-24 12:12:06 +01:00
Adam Hathcock ad44a7cdbc Merge branch 'dev' into main-dev 2025-04-24 12:01:32 +01:00
Adam Hathcock 38449dca9a Update dependencies (#285) 2025-04-24 11:59:26 +01:00
Adam Hathcock 764eb43838 Merge branch 'dev' into main-dev 2025-04-24 11:52:02 +01:00
Adam Hathcock a84e6d89ca chore(dev) Update to csharpier 1.0 (#284)
* Update to csharpier 1.0

* Fix check and nowarn

* format
2025-04-24 10:45:09 +00:00
Adam Hathcock 377829adae fix(main) exception test correction and token usage (#283)
.NET Build and Publish / build (push) Has been cancelled
* add parallelism on exception after count test

* use scoped token source correctly

* format

* Centralized token usage and made sqlite busy timeout be 5 seconds

* restore write parallelism to 4

* add to comment
2025-04-24 10:40:46 +00:00
Adam Hathcock a479440b66 Merge pull request #286 from specklesystems/adam/check-registration
feature (dev) check registration
2025-04-24 11:29:27 +01:00
Adam Hathcock cc9639b179 Merge pull request #282 from specklesystems/adam/error-fix
fix(main) Wrong error message being displayed in UI
2025-04-24 11:29:10 +01:00
Adam Hathcock d44b4fa52b add additional test 2025-04-24 08:33:42 +01:00
Adam Hathcock ea6ca8c555 add registration tests to SDK and ignore classes that can't be made 2025-04-23 15:57:03 +01:00
Adam Hathcock 113f0fd551 Sanitize test references 2025-04-23 15:42:57 +01:00
Adam Hathcock bcc4e25970 Merge pull request #280 from specklesystems/main-dev
Main->Dev
2025-04-23 13:04:37 +01:00
Adam Hathcock b733ce5f29 fix snapshot test message 2025-04-22 11:09:50 +01:00
Adam Hathcock 1c8b2b82d7 Wrong error message being displayed in UI 2025-04-22 10:13:59 +01:00
Jedd Morgan 11cd2dc1cb Update ProjectResourceExceptionalTests.cs (#279) 2025-04-11 12:27:04 +00:00
Adam Hathcock 3789898ea2 Merge pull request #278 from specklesystems/dev
.NET Build and Publish / build (push) Has been cancelled
SDK 3.2 release from dev to main
2025-04-09 12:35:06 +01:00
Adam Hathcock 0b3318f9e1 Merge pull request #277 from specklesystems/main-dev
Main to dev again
2025-04-09 11:56:35 +01:00
Adam Hathcock 7589ad5f05 Merge remote-tracking branch 'origin/dev' into main-dev 2025-04-09 11:45:06 +01:00
Adam Hathcock 6df32262bd fix: Update HostAppVersion.cs (#276)
Adding v2026 for next autodesk releases support.

Co-authored-by: Jonathon Broughton <760691+jsdbroughton@users.noreply.github.com>
2025-04-09 10:42:40 +00:00
Adam Hathcock 001ca1c287 Update some dependencies (#275) 2025-04-09 10:31:23 +00:00
Adam Hathcock 59e459559b Merge branch 'dev' into main-dev 2025-04-09 11:22:51 +01:00
Adam Hathcock d305fe59cb feat(sdk) clean up registration of sdk to not be connector specific (#274)
* First pass of ObjectSaver and better in-memory usage

* fix some tests

* add commit to match deserialize process

* correct more tests

* format

* make a deserialize factory

* fix tests? and format

* use distinct

* Fix mismerge

* Fix serialization issues with tests

* fix merges

* follow copilot suggestions

* remove disables

* change registration to take strings and TypeLoader isn't public

* remove unused transports

* more test fixes

* fmt

* add Application object back
2025-04-08 09:49:31 +00:00
Adam Hathcock f163b2822e (feat) add memory serialize and make relevant tests use it (#252)
* First pass of ObjectSaver and better in-memory usage

* fix some tests

* add commit to match deserialize process

* correct more tests

* format

* make a deserialize factory

* fix tests? and format

* use distinct

* Fix mismerge

* Fix serialization issues with tests

* fix merges

* follow copilot suggestions

* remove disables
2025-04-08 10:21:47 +01:00
Adam Hathcock 630bb38b8b fix: Update HostAppVersion.cs (#273)
Adding v2026 for next autodesk releases support.

Co-authored-by: Jonathon Broughton <760691+jsdbroughton@users.noreply.github.com>
2025-04-07 13:43:22 +01:00
Adam Hathcock bacff2da34 Merge branch 'dev' into main-dev 2025-04-07 12:46:07 +01:00
Adam Hathcock 129d5285ed Merge branch 'main' into main-dev 2025-04-07 12:45:09 +01:00
Adam Hathcock 5d07fe0ea0 refactor error handling to no propagate through channels. (#268)
* refactor error handling to no propagate through channels.  Use cancellation to shut down on exception

* don't try to shutdown scheduler when exception happens...handle tokens in a tree

* some cleanup

* ConfigureAwait(false) again

* Add test and clean up exception handling

* fmt

* fixed tests
2025-04-07 11:49:47 +01:00
Adam Hathcock 062e3c2838 Make packs require tests too (#254) 2025-04-07 10:10:41 +00:00
Adam Hathcock f4cc4bc77e Merge pull request #272 from specklesystems/adam/add-2026
.NET Build and Publish / build (push) Has been cancelled
fix: Update HostAppVersion.cs for 2026
2025-04-07 10:55:25 +01:00
Jonathon Broughton d395f03219 fix: Update HostAppVersion.cs
Adding v2026 for next autodesk releases support.
2025-04-07 10:10:18 +01:00
Adam Hathcock 56ed399d70 Removes log statement that is confusing people when viewing log files (#267) 2025-03-26 14:21:06 +00:00
Adam Hathcock 7e0766fc7f Merge pull request #262 from specklesystems/main-dev
Main to dev
2025-03-25 10:24:16 +00:00
Adam Hathcock 0df343ebe1 Merge remote-tracking branch 'origin/main' into main-dev 2025-03-25 09:02:02 +00:00
Oğuzhan Koral 3cad57ceb3 Update object instead save (#266)
.NET Build and Publish / build (push) Has been cancelled
2025-03-24 20:24:15 +03:00
Oğuzhan Koral 11937ad7c9 Handle refresh token on catch (#265)
.NET Build and Publish / build (push) Has been cancelled
2025-03-24 13:39:23 +03:00
Adam Hathcock 8210fde69a Merge pull request #264 from specklesystems/oguzhan/parameter-for-app-id-secret
.NET Build and Publish / build (push) Has been cancelled
Chore(accounts): Extract hard coded app as parameter into function
2025-03-20 15:29:50 +00:00
oguzhankoral fa6f90621e Extract hard coded app as parameter into function 2025-03-20 18:18:23 +03:00
Adam Hathcock f6f1852664 Merge branch 'dev' into main-dev 2025-03-17 12:36:33 +00:00
Adam Hathcock b3f4190614 Merge pull request #260 from specklesystems/adam/fix-cancel-check
.NET Build and Publish / build (push) Has been cancelled
Cancellation check for sending needs to not be wrapped
2025-03-17 12:21:44 +00:00
Adam Hathcock 2fc0024cd2 Cancellation check for sending needs to not be wrapped 2025-03-17 12:08:42 +00:00
Adam Hathcock 300a5627fd Merge pull request #259 from specklesystems/revit-levels
.NET Build and Publish / build (push) Has been cancelled
feat(objects): adds level to revitobject
2025-03-17 11:06:13 +00:00
Claire Kuang 22f029fe33 Update RevitObject.cs 2025-03-17 10:32:47 +00:00
Claire Kuang c728266c88 Update RevitObject.cs 2025-03-17 10:31:43 +00:00
Claire Kuang 7f2d57cdad adds level 2025-03-17 10:05:41 +00:00
Adam Hathcock 81a22bd4cc Merge pull request #256 from specklesystems/main-dev
Main to dev
2025-03-13 10:39:11 +00:00
Adam Hathcock f64da099ab Merge branch 'dev' into main-dev 2025-03-13 08:48:36 +00:00
Adam Hathcock f4ba200640 Merge branch 'main' into main-dev 2025-03-13 08:47:23 +00:00
Adam Hathcock 4e84766be9 Merge pull request #253 from specklesystems/regions-PR-to-main
.NET Build and Publish / build (push) Has been cancelled
2025-03-12 17:55:01 +00:00
KatKatKateryna 73a95aded0 Regions class (#241)
* region class

* comments

* adjusted constructor

* todos

* comment

* change displayValur to curves

* small refactor

* assign correct property

* generalize display value

* some minor xml changes

* transform meshes; bbox not required

* remove comment

* Update Region.cs

---------

Co-Authored-By: Claire Kuang <kuang.claire@gmail.com>
Co-Authored-By: Adam Hathcock <adamhathcock@users.noreply.github.com>
2025-03-12 17:38:28 +00:00
KatKatKateryna 404600a839 Regions class (#241)
* region class

* comments

* adjusted constructor

* todos

* comment

* change displayValur to curves

* small refactor

* assign correct property

* generalize display value

* some minor xml changes

* transform meshes; bbox not required

* remove comment

* Update Region.cs

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
Co-authored-by: Adam Hathcock <adamhathcock@users.noreply.github.com>
2025-03-12 19:10:45 +08:00
Jedd Morgan 93d517eab7 test(objects): Verify tests for serialization (#227)
* Objects Verify Tests

* VerifyTests

* Verify as array with deterministic order

* lock

* Updated verified
2025-03-12 08:29:30 +00:00
Adam Hathcock 4110d90107 Merge pull request #245 from specklesystems/adam/more-code-cov2
Client tests and another SpeckleHttp one
2025-03-11 13:54:14 +00:00
Adam Hathcock 2d134bf7e1 make ExecuteWithResiliencePolicies internal 2025-03-11 13:24:39 +00:00
Adam Hathcock 686d0fd31c Merge remote-tracking branch 'origin/dev' into adam/more-code-cov2 2025-03-11 13:15:32 +00:00
Adam Hathcock b06878bbe1 Merge pull request #242 from specklesystems/remove-more-attributes
Remove more attributes
2025-03-11 12:09:56 +00:00
Adam Hathcock 64114167a8 Merge branch 'dev' into adam/more-code-cov2 2025-03-11 12:07:03 +00:00
Adam Hathcock 0c7cd75353 Merge branch 'dev' into remove-more-attributes 2025-03-11 11:58:47 +00:00
Adam Hathcock 3c5679ad2a Merge pull request #251 from specklesystems/main-dev
Main to dev
2025-03-11 11:58:12 +00:00
Adam Hathcock b043623021 Merge remote-tracking branch 'origin/dev' into main-dev 2025-03-10 09:36:28 +00:00
Adam Hathcock bd0997913a Add back the integration test 2025-03-10 09:31:56 +00:00
Adam Hathcock ac0ab3904c Merge pull request #250 from specklesystems/fix-main
.NET Build and Publish / build (push) Has been cancelled
Fix main again: version the nuget correctly
2025-03-10 09:03:53 +00:00
Adam Hathcock 5c9d672a2b Really fix publish version 2025-03-10 08:50:52 +00:00
Jedd Morgan a79f0fd035 Deprecated public visibility (#249) 2025-03-08 17:50:52 +00:00
Adam Hathcock 18d6653282 Merge pull request #247 from specklesystems/fix-main
.NET Build and Publish / build (push) Has been cancelled
Fix main
2025-03-07 14:54:53 +00:00
Adam Hathcock d58a656c8a make private 2025-03-07 14:43:48 +00:00
Adam Hathcock 720d049145 format 2025-03-07 14:35:37 +00:00
Adam Hathcock 129688a646 Fix restore tools and comment out integration test for now 2025-03-07 14:34:24 +00:00
Adam Hathcock fa66258b23 Merge pull request #246 from specklesystems/release-to-main
Release to main
2025-03-07 14:27:19 +00:00
Adam Hathcock 9f0c572993 also on tags 2025-03-07 14:01:19 +00:00
Adam Hathcock 158baff5b0 Merge branch 'update-build-stamp' into release-to-main 2025-03-07 14:01:10 +00:00
Adam Hathcock 7a29d27e46 Merge remote-tracking branch 'origin/dev' into adam/more-code-cov2 2025-03-07 11:14:32 +00:00
Adam Hathcock 82e3d37dd1 graphql test with client 2025-03-07 11:12:31 +00:00
Adam Hathcock 9695ec8c51 SpeckleHttp coverage (#244)
* Show code coverage for dev

* SpeckleHttp coverage and moved base stuff around

* add SpeckleHttpTests
2025-03-07 10:48:01 +00:00
Adam Hathcock 15fa319433 Show code coverage for dev (#243) 2025-03-07 10:26:13 +00:00
Adam Hathcock 793bbb9cd3 Complete speckle http 2025-03-07 09:29:05 +00:00
Adam Hathcock 3291010d43 add SpeckleHttpTests 2025-03-07 09:09:25 +00:00
Adam Hathcock 14732ce174 SpeckleHttp coverage and moved base stuff around 2025-03-07 09:03:07 +00:00
Adam Hathcock ba655988b0 Show code coverage for dev 2025-03-07 08:39:51 +00:00
Adam Hathcock 96822c4e66 Fix tests 2025-03-06 16:55:01 +00:00
Adam Hathcock 08356de1ad Merge remote-tracking branch 'origin/dev' into remove-more-attributes 2025-03-06 13:46:28 +00:00
Adam Hathcock 375f5071ae Improve test coverage by adding tests and removing unused code (#240)
* maybe all objects need to be false

* fmt

* remove extra code form hosts

* fix build

* add speckle serializer exception test

* add DownloadObjects and DownloadSingleObject test

* fmt

* add HasObjects test

* BaseItem tests

* adjust code and add test for UploadObjects

* Remove try/catch

* can't test compressed?  use lib to parse multipart

* remove unused verify snapshots
2025-03-06 13:20:11 +00:00
Adam Hathcock 5900a3c178 compiles 2025-03-06 12:00:57 +00:00
Adam Hathcock d7bf324029 Remove more unused attributes 2025-03-06 11:59:13 +00:00
Adam Hathcock c784fbf462 remove unused verify snapshots 2025-03-05 14:14:42 +00:00
Adam Hathcock 26f1802787 can't test compressed? use lib to parse multipart 2025-03-05 14:13:05 +00:00
Adam Hathcock a96e0f8c8e Remove try/catch 2025-03-05 12:38:34 +00:00
Adam Hathcock 9f36c9cfe5 adjust code and add test for UploadObjects 2025-03-05 12:18:19 +00:00
Adam Hathcock 4959f277e8 BaseItem tests 2025-03-05 11:10:21 +00:00
Adam Hathcock ea08b83f7a add HasObjects test 2025-03-05 09:54:31 +00:00
Adam Hathcock b24dc685fa fmt 2025-03-05 09:44:32 +00:00
Adam Hathcock 7cad14fe25 add DownloadObjects and DownloadSingleObject test 2025-03-05 09:34:44 +00:00
Adam Hathcock 4d552b6834 add speckle serializer exception test 2025-03-04 16:44:15 +00:00
Adam Hathcock 378a91995e fix build 2025-03-04 16:41:59 +00:00
Adam Hathcock 53b66dd26b remove extra code form hosts 2025-03-04 13:29:36 +00:00
Adam Hathcock 9dd04c0881 fmt 2025-03-03 16:39:17 +00:00
Adam Hathcock 474c18c29f maybe all objects need to be false 2025-03-03 16:32:02 +00:00
Adam Hathcock b676da4ac4 disable .net connection pooling...use our own to free up the files (#238) 2025-03-03 16:07:23 +00:00
Adam Hathcock fe964f4c8e Use IAsyncDisposable on scheduler which also waits for completion (#235)
* Use IAsyncDisposable on scheduler which also waits for completion

* formatting
2025-03-03 14:50:38 +00:00
Adam Hathcock 1fea4cc01b Add a logging statement for when batch size was larger than expected (25 megs) (#232) 2025-02-27 15:44:35 +00:00
Adam Hathcock c3d716f6fd fix: an exception in the underlying code results in UnobservedTaskExceptions and not ending of the send process. (#237)
* Cancel all channels when first exception happens then throw that exception

* Use single exception to end things.

* fix verifications

* fmt

* Fix tests as stacktrace was removed

* Handle one exception and cancel on receive

* moved ThrowIfFailed and made throw speckle exceptions

* Fixed tests

* fmt
2025-02-27 14:04:02 +00:00
Jedd Morgan 236cca2663 Updated docker compose file (#236) 2025-02-19 16:55:34 +00:00
Adam Hathcock f20064c9f0 update usage to read shell instead of file 2025-02-19 15:42:08 +00:00
Adam Hathcock 6f1b22d13a Merge remote-tracking branch 'origin/dev' into update-build-stamp 2025-02-19 15:33:31 +00:00
Jedd Morgan 23f95dd38d Fixed mistake in Version Create Docs (#231) 2025-02-18 09:57:54 +00:00
Adam Hathcock 9eb767a4f6 Revert back to content read. Probably some timeout being hit but reverting for now. (#234) 2025-02-18 09:43:20 +00:00
Jedd Morgan 358680cb9a NET core target to return IReadonlySet for freeze (#233) 2025-02-17 16:56:57 +00:00
Adam Hathcock 87ae033f61 Batches size now affects sending size accurately. Use extensions to … (#230)
* Batches size now affects sending size accurately.  Use extensions to help test this

* format

* add more and use extensions

* Rework to make things clear
2025-02-17 15:52:30 +00:00
Adam Hathcock e5a09155a2 Serialize now waits for the scheduler to be completed before returning (#225)
* Serialize now waits for the scheduler to be completed before returning

* formatting

* wait in deserialize too

* Pass cancellation token to download, refactor how things are passed/created

* responses are now streamed and cancel them earlier

* format

* add manual empty message for sending that definitely knows when the channels are empty

* Fix configure awaits

* more configure await false
2025-02-17 10:59:10 +00:00
Adam Hathcock 988599fbb5 Update ranges to allow higher and update other deps (#229) 2025-02-14 10:07:43 +00:00
Claire Kuang baa5f54edb Merge pull request #217 from specklesystems/jedd/cnx-1114-add-support-for-vertex-normals
Vertex Normals
2025-02-13 12:41:57 +00:00
Jedd Morgan d580fd0bc1 Merge remote-tracking branch 'origin/dev' into jedd/cnx-1114-add-support-for-vertex-normals 2025-02-13 10:56:17 +00:00
Adam Hathcock 2352306269 add ability for objectloader to skip server too. (#228)
* add ability for objectloader to skip server too.  add test so both cache and server can't be skipped

* move testing project to correct dir

* remove extra file
2025-02-13 09:41:07 +00:00
Jedd Morgan f0bafee076 gql api changes (#226) 2025-02-12 13:50:13 +00:00
Adam Hathcock ef19bfa69d fix dependencies 2025-02-10 13:41:39 +00:00
Adam Hathcock 45601f1a2f modify build to have correct version 2025-02-10 13:35:59 +00:00
Adam Hathcock adf3298baf release branch is main 2025-02-10 10:38:58 +00:00
Adam Hathcock 8f5e5f675b fmt 2025-02-10 10:36:50 +00:00
Adam Hathcock 9217e67a9d Merge remote-tracking branch 'origin/dev' into update-build-stamp 2025-02-10 10:27:26 +00:00
Jedd Morgan fe68607a52 Smaller chunks for consistency 2025-02-05 16:47:01 +00:00
Jedd Morgan 88d15a5c9c Merge remote-tracking branch 'origin/dev' into jedd/cnx-1114-add-support-for-vertex-normals 2025-02-05 15:54:47 +00:00
Adam Hathcock 3072047129 Use priority scheduler like serialize process to constrain Task enqueuing (#223)
* Use priority scheduler like serialize process to constrain Task enqueuing

* renamespace

* Fix tests and format
2025-02-05 15:36:26 +00:00
Adam Hathcock 316379b04c Update to AwesomeAssertions and update verify (#224)
* Update to AwesomeAssertions and update verify

* attempt to solve disposal problem
2025-02-05 14:03:28 +00:00
Adam Hathcock b056d98645 don't dispose the blocking collection when cancelling (#222)
* don't dispose the blocking collection

* dispose deserialize process correctly

* don't use enumerable and token

* format
2025-02-04 13:14:31 +00:00
Oğuzhan Koral 7329155d7a Fix: Return fast before traverse for wrong ids (#221)
* Return fast before traverse for wrong ids

* add and freeze all ids for a shortcut lookup

* add comment

---------

Co-authored-by: Adam Hathcock <adam@hathcock.uk>
2025-02-03 14:35:33 +00:00
Jedd Morgan c9a5103da7 added workflows back into sln (#219) 2025-02-03 12:49:59 +00:00
Adam Hathcock dbdf4892db dispose BC only when all threads are done. (#220)
* dispose BC only when all threads are done.

* format
2025-02-03 12:10:29 +00:00
Adam Hathcock 3aa993cecb Add cancellation tests (#218)
* Don't log cancelling

* redo exception handling for receive

* remove null test case

* clean up with Id/Json and more cancels

* Change the exception stacks

* fix serialization test

* make a custom scrubber for internalized exceptions

* clean up

* fix namespaces again :(

* adjust the scrubber

* try to make tests more predictable

* rework exceptions again

* strip out compile files used

* formatting

* custom exception validation

* fix init

* Move serialization to own class

* save serialize test

* add deep clean

* add cancellation test on save to cache

* cancellation tests

* format

* do DI correctly

* receive cancel works
2025-01-30 13:42:15 +00:00
Jedd Morgan 327f05adca Merge branch 'dev' into jedd/cnx-1114-add-support-for-vertex-normals 2025-01-27 13:40:48 +00:00
Jedd Morgan ec0d4bf1e3 Vertex Normals 2025-01-27 13:40:30 +00:00
Jedd Morgan 73afa28026 Reamed workflow actions for clarity (#215) 2025-01-27 13:35:06 +00:00
Jedd Morgan f0c7169be8 Small QOL tweaks to Version.Create and ProjectModelsFilter (#210)
* Version Create now returns a Version

* ProjectModelsFilter now has optional args
2025-01-24 12:19:54 +00:00
Jedd Morgan ee10e9d3fc author on Model is now made nullable (#214) 2025-01-24 12:09:12 +00:00
Adam Hathcock 93912d6712 Fix cancellation in schedular. It's no longer optional (#213)
* Fix cancellation in schedular.  It's no longer optional

* formatting

* fix tests because of stacktrace
2025-01-24 11:24:51 +00:00
Adam Hathcock f81fc97a91 Add exception handling for SerializeProcess with CancellationTokenSource (#211)
* Add exception handling for SerializeProcess with CancellationTokenSource

* formatting

* add exception test to make sure we handle a server exception

* add extra exception and handling to stop

* add comment and another test

* one last chance for user to cancel

* formatting
2025-01-23 17:06:08 +00:00
Claire Kuang cc23c147be Merge pull request #200 from specklesystems/claire/cnx-931-create-archicadobject-in-speckle-sharp-sdk
feat(objects): cnx 931 create archicadobject
2025-01-22 11:36:33 +00:00
Claire Kuang 02892a96fe Update ArchicadObject.cs 2025-01-21 20:51:19 +00:00
Claire Kuang ba143fe44b Merge branch 'dev' into claire/cnx-931-create-archicadobject-in-speckle-sharp-sdk 2025-01-21 18:02:07 +00:00
Jedd Morgan 3a71a10cf1 First pass increasing test coverage (#209)
* point tests

* Mesh Tests and removal of unused functions

* Vector tests

* more tests
2025-01-21 14:19:10 +00:00
Adam Hathcock bafd130ece Snapshot testing (#208)
* snapshot testing with verify

* formatting

* add back old serialization tests

* pass verify

* use json correctly

* formatting

* Don't use Quibble and order ourselves because ordering doesn't matter

* whitespace on snapshot

* Better json diffing?  Quibble is back

* add common project

* add object unit tests to see how verify would work

* format

* move random exes to new solution folder

* update lock files
2025-01-16 13:46:53 +00:00
Claire Kuang f79ae7e058 Merge pull request #202 from specklesystems/jedd/cxpla-154-enforce-nullability-analysers-in-objects-and-use-required
Jedd/cxpla 154 enforce nullability analysers in objects and use required
2025-01-15 18:54:08 +00:00
Claire Kuang 77170bf9f3 Merge branch 'dev' into jedd/cxpla-154-enforce-nullability-analysers-in-objects-and-use-required 2025-01-15 17:19:07 +00:00
Adam Hathcock 69167dc206 update some deps and add locks to versions for known packages (#207) 2025-01-15 16:11:04 +00:00
Adam Hathcock 19a5ccfb74 Merge branch 'dev' into claire/cnx-931-create-archicadobject-in-speckle-sharp-sdk 2025-01-14 08:44:47 +00:00
Adam Hathcock b3c6a75387 add a task.yield for a cached model to let progress happen (#204) 2025-01-14 08:44:10 +00:00
Oğuzhan Koral c6556fa2ef Merge branch 'dev' into claire/cnx-931-create-archicadobject-in-speckle-sharp-sdk 2025-01-14 10:37:40 +03:00
Claire Kuang 3bc40fb0bb removes classification and adds elements prop 2025-01-13 15:55:09 +00:00
Adam Hathcock 4370af5376 Allow SDK to report requested downloaded objects and minor adjustments (#203)
* Allow SDK to report requested downloaded objects and minor adjustments

* Remove xyz get server info test
2025-01-13 12:48:28 +00:00
Jedd Morgan 912971d8de reverted unnecessary change to DataObject 2025-01-10 18:29:57 +00:00
Jedd Morgan 9318d85cc8 Merge remote-tracking branch 'origin/dev' into jedd/cxpla-154-enforce-nullability-analysers-in-objects-and-use-required 2025-01-10 15:34:19 +00:00
KatKatKateryna deddb216e5 Gis objects cleaning (#181)
* add Polygon to Geometry; add GisObject

* cleaning objects

* removed final GIS classes

* remove IGisFeature interface

* required keywords for CRS

* required boundary; renaming

* delete the rest of the classes

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2025-01-10 15:33:12 +00:00
Jedd Morgan 465d69ba00 Enforce correct nullability with required 2025-01-10 13:37:18 +00:00
Adam Hathcock 14d959834f Convert to Xunit (#196)
* xunit unit tests

* most pass with formatting

* convert objects to xunit

* remove nunit

* format

* merge fixes

* switch objects to fluent assertions

* update to fluent assertions

* more FA

* convert all to FA

* Format

* Fix tests

* formatting

* hopefully made credential test better

* Catch more specific exception

* use another more specific exception

* Fix tests

* update to xunit

* update packages
2025-01-09 15:32:28 +00:00
Adam Hathcock 465f635142 Add details to sqlite exceptions (#198)
* add ServerObjectManagerFactory

* add usage of a command pool

* add more disposal

* save saving increase

* fix tests

* fixes

* push out concurrency and disposablity

* Add a custom task scheduler

* Better usage, don't wait to enqueue to save to channels

* Completely pre-cal batch size to avoid spinning issues

* Try to fix cache counting

* properly dispose things

* format

* clean up

* adjust count and save on current thread

* move batch it's own file

* update a few packages

* fix build and add batch tests

* revert and format

* Revert "save saving increase"

This reverts commit 3b50c857fb.

* revert change

* adjust and add tests

* Dispose sqlite manager properly

* Make Batch a IMemoryOwner to allow for pooling

* Fix tests

* Upgrade some deps

* try to make tests more explicit

* remove return value

* Add detailed SqLiteJsonCacheException

* details changes
2025-01-09 11:04:14 +00:00
Adam Hathcock ed5bdc91ed Sqlite pooling for connections and commands (#193)
* add ServerObjectManagerFactory

* add usage of a command pool

* add more disposal

* save saving increase

* fix tests

* fixes

* push out concurrency and disposablity

* Add a custom task scheduler

* Better usage, don't wait to enqueue to save to channels

* Completely pre-cal batch size to avoid spinning issues

* Try to fix cache counting

* properly dispose things

* format

* clean up

* adjust count and save on current thread

* move batch it's own file

* update a few packages

* fix build and add batch tests

* revert and format

* Revert "save saving increase"

This reverts commit 3b50c857fb.

* revert change

* adjust and add tests

* Dispose sqlite manager properly

* Make Batch a IMemoryOwner to allow for pooling

* Fix tests

* Upgrade some deps

* try to make tests more explicit

* remove return value

* Use named tuple for all objects
2025-01-08 11:04:32 +00:00
Claire Kuang 0023d2ca2a Merge branch 'dev' into claire/cnx-931-create-archicadobject-in-speckle-sharp-sdk 2025-01-06 14:23:42 +00:00
Claire Kuang ab487991b6 Update ArchicadObject.cs 2025-01-06 14:07:12 +00:00
Claire Kuang 142eefdf39 adds archicad data object and interface
also removes unused gis classes
2025-01-06 14:05:28 +00:00
Adam Hathcock ebccf6ff79 update build stamp and tooling 2025-01-03 16:22:58 +00:00
Adam Hathcock 11fe8e8cce Use CancellationSource to properly dispose of threads in PriorityScheduler (#197)
* Use CancellationSource to properly dispose of threads in PriorityScheduler

* formatting
2025-01-03 14:25:29 +00:00
Adam Hathcock 1fe1a54dcf Custom task scheduler for serialization, fix batch size calc (#194)
* Add a custom task scheduler

* Better usage, don't wait to enqueue to save to channels

* Completely pre-cal batch size to avoid spinning issues

* Try to fix cache counting

* properly dispose things

* format

* clean up

* adjust count and save on current thread

* move batch it's own file

* update a few packages

* fix build and add batch tests
2025-01-03 12:26:19 +00:00
Adam Hathcock a1b9030dba Fixes Markdown to be non HTML based for github and nuget (#195) 2025-01-02 10:12:46 +00:00
Adam Hathcock eec400d0cf Fix DataObject inheritance (#192) 2024-12-19 14:34:15 +00:00
Adam Hathcock 991b31265f Fix data chunks getting written with closure tables (#191)
* Never write closures for datachunks

* add tests and format

* add another test with array and reuses reference

* Remove a writeline in tests
2024-12-17 16:49:17 +03:00
Adam Hathcock 675d896e0d Add method to replace in sqlite (#190) 2024-12-16 15:01:53 +03:00
Adam Hathcock defcee165a Closures are kept for children instead of global (#189)
* disable channels when skipping things

* pass child closures to current.  Current closures out to parent.

* fix build

* adjust options

* use a dictionary pool and pool correctly

* add pools for data chunks

* format
2024-12-13 11:00:21 +00:00
Adam Hathcock 722df50d34 Fix sending caching (#188)
* add list pool creation

* add back base cache for closures

* use bounded channels and tell them to wait

* format and add exception

* adding back json cache?

* nevermind

* format

* add option to skip total finding

* add some comments
2024-12-10 11:21:12 +00:00
Björn Steinhagen 431bb459fc etabs versions (#187)
- support for etabs v21 and v22
- sap 2000 is already covered with v25 and v26
2024-12-05 23:20:45 +03:00
Adam Hathcock a393f7ca8b add explicit interface tests (#186) 2024-12-05 11:29:44 +00:00
Claire Kuang 650fad3b22 Merge pull request #185 from specklesystems/claire/cnx-849
feat(objects): cnx 849 finalized DataObjects
2024-12-04 12:31:07 +00:00
Claire Kuang 81007e0ecf pr fixes 2024-12-04 12:14:17 +00:00
Claire Kuang d29450f0a9 Merge branch 'claire/cnx-849' of https://github.com/specklesystems/speckle-sharp-sdk into claire/cnx-849 2024-12-03 22:30:33 +00:00
Claire Kuang 1e5bf903a2 update tests 2024-12-03 22:30:24 +00:00
Claire Kuang 878edf7c8f Merge branch 'dev' into claire/cnx-849 2024-12-03 22:18:24 +00:00
Claire Kuang ab0ff7b792 removes unnecessary classes 2024-12-03 22:13:46 +00:00
Claire Kuang b5999e3361 adds properties field to DataObject 2024-12-03 19:48:24 +00:00
Adam Hathcock d904c68c10 Nullable fixes - nullability on Base (#184)
* Remove operator overloading and Equals/GetHashCode which caused problems

* properly have nullability annotations on Base

* redeux known nullables

* fmt

* more nullability

* fmt

* remove point equality operator

* Revert "Remove operator overloading and Equals/GetHashCode which caused problems"

This reverts commit 6b5d3f1462.

* Revert "remove point equality operator"

This reverts commit 9b14330147.

* revert nullable accounts

* fix build issues
2024-12-03 13:01:33 +00:00
Adam Hathcock daec64cf8e Remove the Base cache because it's not practically useful (#183) 2024-12-03 12:48:18 +00:00
Adam Hathcock 1844386235 Better options for serializer (#180)
* refactor options to use on factories, add dummies

* format

* reduce lists to yield return

* get primitives first to avoid to string

* fmt

* add cache base option
2024-11-29 15:43:10 +00:00
Adam Hathcock 05f90eae5c Use frozen collections (#178)
* add detail to exceptions

* Detachable nested objects work

* fmt

* Use frozen collections

* fix tests and formatting
2024-11-29 15:25:29 +00:00
Adam Hathcock 7bbc568022 Make ISqLiteJsonCacheManager and use it for new serialization and account manager (#179)
* Introduce ISqLiteJsonCacheManager with factory for cache manager and other uses

* AccountManager uses new sqlite

* format

* add extension
2024-11-27 12:14:16 +00:00
Adam Hathcock 7a1edbbf99 Fix externals refs again (#177)
* add detail to exceptions

* Detachable nested objects work

* fmt
2024-11-26 09:47:44 +00:00
Dimitrie Stefanescu 058e3cb946 Merge pull request #176 from specklesystems/claire/cnx-793-revit-cleanup-align-with-data-extraction-workflow
cleanup(objects): removes unused revit classes
2024-11-22 17:43:20 +00:00
Claire Kuang 08dd45cc32 Update Interfaces.cs 2024-11-22 12:28:14 +00:00
Claire Kuang d4173c1449 adds civil and tekla objects 2024-11-22 10:21:06 +00:00
Adam Hathcock 4b7c6e4641 fixed test to allow for new detachments 2024-11-22 09:52:59 +00:00
Adam Hathcock 2477423ba7 some tests adjusted 2024-11-22 09:12:10 +00:00
Claire Kuang 71ec9a6d11 rescopes data object classes 2024-11-21 17:53:37 +00:00
Claire Kuang 140b39f605 adds data object classes 2024-11-21 16:47:34 +00:00
Claire Kuang 1a0522cef3 removes unused revit classes 2024-11-20 18:26:38 +00:00
Adam Hathcock b343d9bd0d add Id and Json types (#165)
* Can debug dependencies

* Different exceptions

* Uses root id only after we found it to signal the end

* DataChunks are created later and need to be accounted for

* format

* use app ids in tests and references

* check sqlite cache after serialize

* use dummy to go through channels to end

* fmt

* Extend channel lib to batch by size

* fmt

* build fix

* adjust limits

* FIx sending

* Optimize reference generation

* more

* remove tolist

* rework closures to be constant and serializer only deals with current....references bases are cached

* fix chunk creation

* another bug fix

* clean up with factories

* add deserializer factory

* Needed to reference interface

* add Id and Json types

* Fix tests

* remove optional

* add another missing id write

* fix merge

* Fix new code
2024-11-20 09:51:10 +00:00
Adam Hathcock e32ec52071 Add closures for external references (#175)
* allow external references and fix closures

* format
2024-11-20 08:28:33 +00:00
Adam Hathcock 2bbe2a8b93 allow external references and fix closures (#174)
* allow external references and fix closures

* fix comment
2024-11-19 14:22:20 +00:00
Adam Hathcock 604e141468 Update CSharpier with better new usage (#172) 2024-11-19 14:10:30 +00:00
Jedd Morgan 39dcc5da0b Refactor MaybeThrowGraphqlException to throw AggregateException with all GraphQL errors (#169)
* first pass

* Updated tests

* Removed path from message
2024-11-19 13:22:52 +00:00
Jedd Morgan af35edf0a2 Breaking API changes for comments API (#173)
* Fixed breaking change to comments api

* fixed stuff

* oops
2024-11-19 12:15:17 +00:00
Jedd Morgan fbc7400524 Changed http handler to not throw on non-successful results (#170)
Co-authored-by: Adam Hathcock <adamhathcock@users.noreply.github.com>
2024-11-19 10:55:27 +00:00
dependabot[bot] 8a71b19f78 Bump codecov/codecov-action from 4 to 5 (#171)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 09:33:26 +00:00
Jedd Morgan 105395e9fb Enforced msbuild warnings (#166)
* Enforced msbuild warnings

* removed inline suppression

* fix
2024-11-18 10:20:00 +00:00
Adam Hathcock 715bb7274a Batch by size, Closures and Detached items are computed differently (#164)
* Can debug dependencies

* Different exceptions

* Uses root id only after we found it to signal the end

* DataChunks are created later and need to be accounted for

* format

* use app ids in tests and references

* check sqlite cache after serialize

* use dummy to go through channels to end

* fmt

* Extend channel lib to batch by size

* fmt

* build fix

* adjust limits

* FIx sending

* Optimize reference generation

* more

* remove tolist

* rework closures to be constant and serializer only deals with current....references bases are cached

* fix chunk creation

* another bug fix

* clean up with factories

* add deserializer factory

* Needed to reference interface

* move around streamId

* some clean up

* Use StringBuilder pool on serialization to reduce memory pressure

* remove extra

* remove extra clears

* Fix a flaw in batchsize

* use default complete

* format

* loader should use 1 writer that is batched

* remove redundant ref gen

* Fix graphql commands by adding project id
2024-11-12 14:25:49 +00:00
Adam Hathcock 43445bc4ff Add SQLite transport2 (#162)
* Add SQLiteTransport2

* cleanup
2024-11-07 10:00:43 +00:00
Adam Hathcock 3bff2d5d57 Use Reference generator (#161)
* Optimize reference generation

* more

* use reference generator

* move

* remove using
2024-11-07 09:48:52 +00:00
Adam Hathcock 4784e7d432 Revert "feat: Use 16-core larger runner for github actions (#158)" (#160)
This reverts commit 378149a0af.
2024-11-06 12:19:52 +00:00
Alan Rynne 378149a0af feat: Use 16-core larger runner for github actions (#158) 2024-11-06 11:53:58 +00:00
Adam Hathcock 0240e3b49e Send Channel fixes (#155)
* Can debug dependencies

* Different exceptions

* Uses root id only after we found it to signal the end

* DataChunks are created later and need to be accounted for

* format

* use app ids in tests and references

* check sqlite cache after serialize

* use dummy to go through channels to end

* fmt

* fix tests and add more send events
2024-11-06 11:53:21 +00:00
Jedd Morgan d65993389b Obsolete warnings (#156) 2024-11-06 11:29:40 +00:00
Adam Hathcock 4cc78c4bc9 Serialize using a Channel (#146)
* Use a stack channel for deserialization

* multi-threaded

* add object dictionary pool

* more pooling

* adjust sqlite transport

* format

* Optimize IsPropNameValid

* object loader first pass

* save test

* add cache pre check

* save better deserialize

* mostly works

* uses tasks but slower at end

* rework to make more sense

* add check to avoid multi-deserialize

* modify max parallelism

* async enqueuing of tasks

* switch to more asyncenumerable

* fmt

* fmt

* cleanup sqlite

* make ServerObjectManager

* revert change

* add ability to skip cache check

* cache json to know what is loaded

* testing

* clean up usage

* clean up and added new op

* Fix exception handling

* fixing progress

* remove codejam

* Hides ObjectPool dependency

* fmt

* Use the 1.0 BCL async to try to be more compatible

* rename to dependencies

* Move Polly to internal dependencies

* format

* remove more old references

* remove stackchannel

* fixes for registration

* remove console writeline

* add cache check shortcut for root object

* start refactoring send

* recevie2 benchmark

* add test for deserialize new

* use channels for sending

* test and fixes

* Use same asyncinterfaces as Dynamo.  Merge fixes

* clean up

* fix download object progress

* put back from bad merge

* intermediate commit: separating get child function from serializer

* send didn't error

* add channels

* Use net48, netstandard2.1 and net8

* remove collection special case

* have to make a tree of tasks even though it may serialize things twice

* pre-id changing during serialize

* need AsyncInterfaces for net48 :(

* options changes

* revert to netstandard2.0 and net8.0

* fix totals

* revert httpcontext changes

* format

* clean up

* active tasks works when accounting for id not being stable

* add id tests

* more fixes

* works

* format

* Convert to BaseItem and use single SQLite checks to avoid locks

* use locks and batch sqlite operations

* hook up and handle null ids

* remove unused parameter

* remove progress from serializer itself

* invert has objects call

* readd object references

* format

* fix tests

* remove active tasks check

* bug fix for json cache

* remove locks from sqlite

* General Send test

* add childclosures

* redo extract all to be enumerable

* group tests in projects

* caching json does matter

* cache checking should be managed by channels

* format

* Merge pull request #152 from specklesystems/new-json-test

Uses a new objects test in Revit for serialization tests

* add skip

* add new roundtrip test

* fix finish

* clean up tests

* check happens in serialize...don't do it twice

* better progress reporting

* fix progress reporting

* only use detached properties when children gathering

* move detached tests

* add detached tests

* fix merge

* Fix progress change

* fix more tests

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2024-11-05 09:56:54 +00:00
Claire Kuang 8a148b892f refactor(objects): removes unused classes and constructors (#151)
* removes unused classes and gh schema constructors

* additional cleanup of backwards compatibility (v2)

* Update Arc.cs

* updates unit tests

* re-adds bbox to box

* updates tests

* pr fixes for backwards compatibility

* Uses a new objects test in Revit for serialization tests

* format

* updates revit model curve class to inherit from curve geometry, and adds deprecated v2 classes

* fixes transform unit tests

* fixes deprecated speckle type

* removes unnecessary new revit curve classes

---------

Co-authored-by: Adam Hathcock <adam@hathcock.uk>
Co-authored-by: Alan Rynne <alan@rynne.es>
2024-11-04 17:05:16 +00:00
Jedd Morgan fba0c46ed8 Added explicit nullability to GqlModels (#144)
* Added explicit nullability to GqlModels

* data and alignment

* ModelsWithVersions

* Models

* active user

* Invites and active user fixes

* Invite changes

* Subscriptions

* Fixed subscription tests

* corrected nullability of projectinput

* removed unused mutation responses

* format

---------

Co-authored-by: Alan Rynne <alan@rynne.es>
2024-11-04 16:52:15 +00:00
Claire Kuang 91c4090037 Merge pull request #154 from specklesystems/claire/cnx-699-update-github-links-to-point-to-v3-1
Update README.md to align with main github page
2024-11-04 11:12:27 +00:00
Claire Kuang c2962a668b Update README.md to align with main github page
https://linear.app/speckle/issue/CNX-699/update-github-links-to-point-to-v3
2024-11-01 17:42:31 +00:00
Adam Hathcock 27d3028880 Revert to SQlite 7.0.5 to exactly match V2's usage (#153)
* Revert to SQlite 7.0.5 to exactly match V2's usage

* update comment to be exact
2024-10-31 14:58:52 +00:00
Alan Rynne 93b3c37584 fix: Change SpecklePathProvider from internal to public (#150) 2024-10-28 13:04:34 +01:00
Adam Hathcock f5a8d1a738 Add cache download channels (#149)
* Use a stack channel for deserialization

* multi-threaded

* add object dictionary pool

* more pooling

* adjust sqlite transport

* format

* Optimize IsPropNameValid

* object loader first pass

* save test

* add cache pre check

* save better deserialize

* mostly works

* uses tasks but slower at end

* rework to make more sense

* add check to avoid multi-deserialize

* modify max parallelism

* async enqueuing of tasks

* switch to more asyncenumerable

* fmt

* fmt

* cleanup sqlite

* make ServerObjectManager

* revert change

* add ability to skip cache check

* cache json to know what is loaded

* testing

* clean up usage

* clean up and added new op

* Fix exception handling

* fixing progress

* remove codejam

* Hides ObjectPool dependency

* fmt

* Use the 1.0 BCL async to try to be more compatible

* rename to dependencies

* Move Polly to internal dependencies

* format

* remove more old references

* remove stackchannel

* fixes for registration

* remove console writeline

* add cache check shortcut for root object

* recevie2 benchmark

* add test for deserialize new

* Use same asyncinterfaces as Dynamo.  Merge fixes

* clean up

* fix download object progress

* add channels

* Use net48, netstandard2.1 and net8

* need AsyncInterfaces for net48 :(

* options changes

* revert to netstandard2.0 and net8.0

* fix totals

* revert httpcontext changes

* format

* add active tasks collection

* format

* remove more changes and remove unused method

* remove more comment changes

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2024-10-25 14:28:33 +01:00
Adam Hathcock e10cc38ca7 Give new dependencies lib ability to publish (#148) 2024-10-24 13:53:10 +00:00
Adam Hathcock b6d2d01068 Tasks hide new dependencies (#145)
* Use a stack channel for deserialization

* multi-threaded

* add object dictionary pool

* more pooling

* adjust sqlite transport

* format

* Optimize IsPropNameValid

* object loader first pass

* save test

* add cache pre check

* save better deserialize

* mostly works

* uses tasks but slower at end

* rework to make more sense

* add check to avoid multi-deserialize

* modify max parallelism

* async enqueuing of tasks

* switch to more asyncenumerable

* fmt

* fmt

* cleanup sqlite

* make ServerObjectManager

* revert change

* add ability to skip cache check

* cache json to know what is loaded

* testing

* clean up usage

* clean up and added new op

* Fix exception handling

* fixing progress

* remove codejam

* Hides ObjectPool dependency

* fmt

* Use the 1.0 BCL async to try to be more compatible

* rename to dependencies

* Move Polly to internal dependencies

* format

* remove more old references

* remove stackchannel

* fixes for registration

* remove console writeline

* add cache check shortcut for root object

* recevie2 benchmark

* add test for deserialize new

* Use same asyncinterfaces as Dynamo.  Merge fixes

* clean up

* fix download object progress

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2024-10-24 15:23:16 +02:00
Adam Hathcock cca8828565 Using Tasks for Deserialization (#143)
* Use a stack channel for deserialization

* multi-threaded

* add object dictionary pool

* more pooling

* adjust sqlite transport

* format

* Optimize IsPropNameValid

* object loader first pass

* save test

* add cache pre check

* save better deserialize

* mostly works

* uses tasks but slower at end

* rework to make more sense

* add check to avoid multi-deserialize

* modify max parallelism

* async enqueuing of tasks

* switch to more asyncenumerable

* fmt

* fmt

* cleanup sqlite

* make ServerObjectManager

* revert change

* add ability to skip cache check

* cache json to know what is loaded

* testing

* clean up usage

* clean up and added new op

* Fix exception handling

* fixing progress

* remove codejam

* remove stackchannel

* remove console writeline

* add cache check shortcut for root object

* recevie2 benchmark

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2024-10-22 14:14:39 +01:00
Adam Hathcock 3c783b3cac retry result can be null so handle it better (#142)
Co-authored-by: Alan Rynne <alan@rynne.es>
2024-10-17 09:38:09 +00:00
Jedd Morgan 8f8be7482a Added workspace id to v3 sdk (#134)
* Added workspace id to v3 sdk

* use init setter
2024-10-15 12:57:15 +01:00
Adam Hathcock 3b859ba398 Add headers to outgoing http requests for Open Telemetry (#141)
* Add otel headers to outgoing http client requests

* format
2024-10-14 10:40:29 +01:00
Adam Hathcock 4c52072e0a Optimize IsPropNameValid (#140)
* Optimize IsPropNameValid

* compare char to char
2024-10-11 09:44:45 +00:00
Dimitrie Stefanescu 1241589d18 Adds new classes for raw encoded objects (Brep, Extrusion and Subd) (#139)
* feat: adds wip classes for brep/subd/extrusion-x

* Get rid of dub code on ExtrusionX SubDX BrepX

* fix: Run format

* chore: formatting

* chore: more formatting

help dis annoying

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
Co-authored-by: Alan Rynne <alan@speckle.systems>
Co-authored-by: Adam Hathcock <adamhathcock@users.noreply.github.com>
2024-10-10 09:26:55 +00:00
Adam Hathcock 1a9d28e660 add metrics to sdk (#133)
* add metrics to sdk

* metrics for sends and receives

* format
2024-10-10 10:32:52 +02:00
Adam Hathcock e14913b8c3 Fix deserialization progress reporting (#137)
* Fix progress reporting for deserialization, remove children known count

* fix tests
2024-10-09 11:57:48 +01:00
Alan Rynne 0d7805d060 feat: Added new speckle exceptions and split into single files (#136)
* feat: Added new speckle exceptions and split into single files

* fix: Run format
2024-10-07 17:14:59 +02:00
Adam Hathcock 14359333b4 Nuget Updates (#132)
* update test packages

* update csharpier
2024-10-07 12:48:00 +02:00
Adam Hathcock 75d88c5f39 Use IProgress instead of custom type (#131)
* Use IProgress instead of custom type

* Format

* ProgressArgs are a readonly struct

* fix tests
2024-10-02 10:50:49 +01:00
Adam Hathcock 9a3eddf6d5 use array defaults (#129) 2024-09-27 11:00:21 +01:00
Jedd Morgan f541525a80 Regex experimentation (#128) 2024-09-26 13:58:48 +01:00
Adam Hathcock c3e8a95bf1 Merge pull request #126 from specklesystems/some-optimizations
Some  allocation optimizations
2024-09-26 12:51:13 +01:00
Adam Hathcock c2d0e1d0d4 use await instead of .Result 2024-09-26 08:13:58 +01:00
Adam Hathcock f0d83d8d43 Merge remote-tracking branch 'origin/dev' into some-optimizations 2024-09-26 08:13:04 +01:00
Adam Hathcock ba14a8c87f Use pool more 2024-09-25 16:35:58 +01:00
Jedd Morgan 065a2318d3 Jedd/cxpla 84 add commit context to receive trace (#125)
* Smells like v2

* Fixed issue with cancellation of ParallelServerApi

* activity ok

* Serialize json

* settags extension method

* Isbusy fix

* Final polish

* added CallerMemberName

* removed absractions
2024-09-25 16:35:38 +01:00
Adam Hathcock 40d025bb57 use pool and more ValueTask 2024-09-25 16:24:57 +01:00
Adam Hathcock ddeac27f04 Use pool and remove some async 2024-09-25 16:20:31 +01:00
Adam Hathcock d73bf365c2 Clean app plugin registration and all speckle plugin verisons (#123)
* Clean app plugin registration and all speckle plugin verisons

* fix naming
2024-09-20 09:32:29 +02:00
Adam Hathcock 198f1a2564 Remove STJ dependency (#122) 2024-09-19 14:11:16 +01:00
Jedd Morgan 694e9e1915 Fixed issue with cancellation of ParallelServerApi (#121) 2024-09-18 09:06:53 +00:00
Adam Hathcock 0f29aacf24 Making SDK non-static (#116)
* initial burn

* fix: Minor fixes, some pending

* fix: Minor fix + DeepClean target

* setup things to be injected

* things compile and activites are instanced

* compiles except tests

* tests compile

* fmt

* fix merge

* Activities are now an interface

* fmt

* fully remove logging DLL

* clean up

* fix tests

* more cleanup

* Fix integration tests

* clean up hash tests

* clean up path provider

* more cleanup for path

* merge fixes

* Merge fixes

* remove some extra classes

---------

Co-authored-by: Alan Rynne <alan@speckle.systems>
2024-09-17 15:51:50 +01:00
Jedd Morgan a28cf6d908 Merge pull request #119 from specklesystems/jrm/main-merge
Update Http Send trace (#118)
2024-09-16 15:42:37 +01:00
Jedd Morgan 2cb5d6e410 Fix using directives 2024-09-16 13:43:43 +01:00
Jedd Morgan 59628d5241 Merge remote-tracking branch 'origin/dev' into jrm/main-merge 2024-09-16 13:39:05 +01:00
Adam Hathcock bbe23f6996 Update Http Send trace (#118)
* Update Http Send trace

* format
2024-09-16 12:45:04 +02:00
Jedd Morgan df5b151c90 Test Coverage (#117) 2024-09-16 08:41:26 +01:00
Jedd Morgan ccb4f54550 Jedd/cxpla 55 add required keyword for more geometry types (#101)
* Sdk

* Objects

* Supressed IDE warnings via editor config instead of nowarn

* Nullability and other warnings

* using

* Required keyword on lines and meshes

* Fixed test project

* fixed tests

* Proxies

* Fixed equality of Point

* IEquatable

* Fixed issue with serialization of detached lists

* Added tests for jsonIgnore affecting id calc

* removed comments

* Fixed issue with fallback to double on large values

* Fixed undocumented large number support
2024-09-13 14:47:29 +01:00
Jedd Morgan 30a3533956 Simplified props files (#115)
* Simplified props files

* Private package
2024-09-12 12:06:50 +00:00
Adam Hathcock e07bfc18d5 Remove async from serializer (#113)
* Remove async from serializer

* Make GetId be sync

* fix chunking?

* Chunking Tests (#114)

* fmt

* Fixed speckle type

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2024-09-11 15:35:59 +01:00
Adam Hathcock 2ad14ca00b Fix serialization of ignored (#112)
* Fixed issue with serialization of detached lists

* Added tests for jsonIgnore affecting id calc

* use extra writer and pass tests

* avoid returning extra object

* fix tests

* Add span overload back

* fmt

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2024-09-10 13:17:46 +01:00
Jedd Morgan 450dbcce81 Sha256 hash with spans (#107)
* Sha256 hash with spans

* HashData

* NumericFormat

* Tests

* md5 test

* xml

* use utf16 encoding rather than utf8

---------

Co-authored-by: Alan Rynne <alan@rynne.es>
2024-09-09 12:43:32 +00:00
Jedd Morgan 296a00e2eb Ralph's suggested changes (#105) 2024-09-09 12:23:49 +00:00
Alan Rynne f085a3fb5a Merge pull request #109 from specklesystems/main
Back Merge main->Dev
2024-09-09 14:18:39 +02:00
Alan Rynne 56c4109210 fix(docker): Update docker-compose.yml server healthcheck (#108)
* fix: Use node location instead of relying on $PATH

* fix: Update to /readiness endpoint instead of /liveness
2024-09-09 11:21:43 +00:00
Adam Hathcock 92bcb51bf2 Disable snupkg for logging (#104) 2024-09-05 17:35:32 +01:00
Adam Hathcock ce3d610ac5 Add CancellationToken usage to Closure parsing (#103)
* Add CancellationToken usage to Closure parsing

* fmt
2024-09-05 16:53:26 +01:00
Adam Hathcock 1be044e442 Remove redundant Contains/GetHashCode (#102) 2024-09-05 15:12:58 +01:00
Adam Hathcock cc7d274f43 Add .NET 8 targets (#97)
* Add targets to csproj and fixed up logging

* Logging compiles

* SDK compiles

* Objects compiles

* fmt

* fix build naming

* remove net6.0

* more null fixes

* merge fixes
2024-09-05 15:12:22 +01:00
Adam Hathcock 0b1de566df Async GetObject and Async Deserialization (#95)
* Use Concurrent Dictionaries and ignore case on them to avoid ToLower

* remove single array allocation

* allocate GetClosures differently

* Use JsonReader for closures

* add comment

* use isdefined

* more readonly with less allocations

* sorts

* fmt

* use element type when making an array

* first pass

* Fix reading for object and array

* serialization works?

* Fix closure parsing

* things are way faster

* Deserialize is async

* fmt

* renames

* remove deserialize threads

* fmt

* faster to use ordinal compare

* serialization looks okay

* fix closure writing

* fixes

* use possibly different values for compute id

* use closure parser on download

* memory test for blobs

* decompose serialization

* fmt

* fmt

* for decomposing, values should be used instead of original

* set id after computing it

* redo more closure parsing

* fix memory test

* don't throw on try get deserialized

* fmt

* fix integration tests

* fix tests

* disable memory blob storage by default

* put back ?

* merge fixes and delete worker threads

* fmt

* serialization of old floats pass

* serialization of old floats pass

* rename class

* Use async/await on GetObject

* await deserialization

* fmt

* uncomment and fix tests

* don't allow things to exist in the closure table that doesn't exist

* detach blob tests

* rename serializer

* async more correct

* revert

* fix merge

* fix blob tests again

* more fixes

* Fix building

* async fixes

* more async fixes

* fix tests?

* rename GetId to GetIdAsync

* clean up

* more cleanup

* fmt

* fix test

* fix analyzer errors

* use ConcurrentDictionary to be thread safe
2024-09-04 13:45:56 +01:00
Jedd Morgan 5e0ea324c3 Re-introduced code analysers and fixed many violations (#92)
* Sdk

* Objects

* Supressed IDE warnings via editor config instead of nowarn

* Nullability and other warnings

* using

* Fixed warnings

* Important fix

* More fixes
2024-09-04 11:49:35 +00:00
Jedd Morgan b9180027c5 Detach Levels (#99) 2024-09-03 18:14:22 +01:00
Adam Hathcock 77a40a3af5 Merge pull request #98 from specklesystems/main
Main to dev
2024-09-03 16:39:07 +01:00
Adam Hathcock 22f4945d71 Json reader deserialize (#88)
* Use Concurrent Dictionaries and ignore case on them to avoid ToLower

* remove single array allocation

* allocate GetClosures differently

* Use JsonReader for closures

* add comment

* use isdefined

* more readonly with less allocations

* sorts

* fmt

* use element type when making an array

* first pass

* Fix reading for object and array

* serialization works?

* Fix closure parsing

* things are way faster

* Deserialize is async

* fmt

* renames

* remove deserialize threads

* fmt

* faster to use ordinal compare

* serialization looks okay

* fix closure writing

* fixes

* use possibly different values for compute id

* use closure parser on download

* memory test for blobs

* decompose serialization

* fmt

* fmt

* for decomposing, values should be used instead of original

* set id after computing it

* redo more closure parsing

* fix memory test

* don't throw on try get deserialized

* fmt

* fix integration tests

* fix tests

* disable memory blob storage by default

* put back ?

* merge fixes and delete worker threads

* fmt

* serialization of old floats pass

* serialization of old floats pass

* rename class

* uncomment and fix tests

* don't allow things to exist in the closure table that doesn't exist

* detach blob tests

* rename serializer

* revert

* fix blob tests again
2024-09-03 15:53:50 +01:00
Jedd Morgan c0f8949705 Jedd/cxpla 33 move performance test project (#87)
* Moved performance test project

* register types

* perf tests
2024-09-02 15:41:48 +00:00
Adam Hathcock 7883cd9d32 Don't use Serilog global static logger in v3 (#96)
* Don't use Serilog global static logger in v3

* make logger be internal only
2024-09-02 13:52:27 +01:00
Alan Rynne b7be9b6fb1 Merge pull request #94 from specklesystems/dev
dev -> main
2024-08-29 13:42:49 +02:00
Jedd Morgan 2b46792a82 Fixed issue with Km unit scalling (#93) 2024-08-28 13:27:42 +02:00
Adam Hathcock 4c3e572bfe clean up from progress (#78)
* progress intermediate commit

* add progress for download

* remove unused code

* remove batch sent callbacks

* multi-threaded deserialize works

* Progress for download and deserialization

* Fix tests

* Have less indeterminate deserialization

* fix deserialization

* make download faster with buffered stream

* put local receive back

* remove unused callback

* fmt

* Progress for serialization and upload

* fix uploading

* clean up from progress

* merge fixes and fmt
2024-08-22 11:18:16 +01:00
Adam Hathcock 0f116ad847 Base caching on TypeLoader (#82)
* Base caching on TypeLoader

* fmt

* add back types
2024-08-22 11:11:53 +01:00
Jedd Morgan 16b1b904af First pass for IBase (#81) 2024-08-21 17:57:33 +00:00
Jedd Morgan 9916048921 Long Send Resiliency (#89)
* LongSendTest

* Moved to separate project

* Server transport throws early

* raised timeouts

* Use poly for timeout

* Cleaned up some of the AsyncPolicy construction

* GraphQL Client back to 30 second timeout
2024-08-21 18:52:23 +01:00
Jedd Morgan 9bea79b3b2 Removed incorrect platform targets (#84)
Co-authored-by: Adam Hathcock <adamhathcock@users.noreply.github.com>
2024-08-19 21:32:23 +01:00
Adam Hathcock af1d7dfaf3 Check for TypeLoader.initialize correctly (#85) 2024-08-19 14:17:45 +01:00
Adam Hathcock 03a5706680 add initial log statement and fix log path (#80) 2024-08-19 12:45:01 +01:00
Adam Hathcock 42882cb71b Perf changes (#79)
* Use Concurrent Dictionaries and ignore case on them to avoid ToLower

* remove single array allocation

* allocate GetClosures differently

* Use JsonReader for closures

* add comment

* use isdefined

* more readonly with less allocations

* sorts

* fmt

* use element type when making an array
2024-08-19 12:05:51 +01:00
Jedd Morgan b14c8db8d3 Required keyword on various types (#76)
* Made bounding boxes nullable

* Required keyword for Polyline

* Curve

* others

* Interval

* Breps!

* more changes

* removed resolved todo comment

* Fixed comment

* resolved alan's comments

* surfaces
2024-08-16 13:21:05 +01:00
Adam Hathcock 2fec0ea791 Cleanup and use concurrent dictionary (#77) 2024-08-15 09:55:08 +01:00
Adam Hathcock dc4da49078 Add Progress for transfers (#74)
* progress intermediate commit

* add progress for download

* remove unused code

* remove batch sent callbacks

* multi-threaded deserialize works

* Progress for download and deserialization

* Fix tests

* Have less indeterminate deserialization

* fix deserialization

* make download faster with buffered stream

* put local receive back

* remove unused callback

* fmt
2024-08-15 10:43:41 +02:00
Adam Hathcock ea5ed874a3 Remove extra SaveObject (#75)
* Remove extra SaveObject

* fmt
2024-08-15 09:30:24 +02:00
Claire Kuang 70729d5ddd feat(gis): CNX-285 new gis feature classes (#73)
* new gis feature classes with interface for attributes

* updates file names to correspond with classes and adds multipatch feature class

---------

Co-authored-by: Adam Hathcock <adamhathcock@users.noreply.github.com>
2024-08-14 15:57:15 +00:00
Adam Hathcock e443840acf Do snupkgs are for Objects and Sdk...not logging (#72) 2024-08-13 20:26:53 +01:00
Adam Hathcock d269ac73dd Disallow SpeckleType inheritance (#71)
* Disallow SpeckleType inheritance

* fmt

* more tests
2024-08-13 14:32:58 +00:00
Jedd Morgan 148a11f83e Fixed source link (#70)
* Fixed source link pdb

* add snupackage
2024-08-13 14:56:04 +01:00
Jedd Morgan 66b3c5e60c Added DUI3 Operations.Send changes to Sdk (#68)
* Added DUI3 Operations.Send changes to Sdk

* Tests

* more tests

---------

Co-authored-by: Dimitrie Stefanescu <didimitrie@gmail.com>
2024-08-12 18:28:35 +01:00
Jedd Morgan cfc4018bd3 removed a bunch of obsolete (#65)
* removed a bunch of obsolete

* Accidental trailing char
2024-08-12 11:49:43 +01:00
Jedd Morgan 4b74ca8607 Some tweaks r.e. JsonIgnore (#64)
* Removed total children count

* Some random tweaks

* JsonIgnore tests

* Ignore base
2024-08-12 09:03:56 +00:00
Adam Hathcock 1c64a975c0 Add detail for OpenTelemetry (#67)
* Add detail for OpenTelemetry

* fmt

* review fixes

* more fixes
2024-08-08 16:04:24 +00:00
Jedd Morgan 78faa71592 Collection name space change (#66) 2024-08-08 12:32:41 +00:00
Adam Hathcock 6f5f044095 Adam/cxpla 6 kill remaining kit code in core (#59)
* compiles with relevant deletions

* Test fixes

* fix type loading

* type load for tests

* speckle objects renamespace

* rename Core to Sdk

* Fix test references

* tests renaming

* rename logging

* fmt

* start of adding an attribute to all base types

* convert all types and do basic test

* Fix most tests

* fix more tests

* fmt

* Build fix

* add changes and more tests

* Fix tests

* Fix integration tests
2024-08-08 10:52:19 +01:00
Jedd Morgan 0b240f0752 Removed total children count (#63) 2024-08-08 08:29:06 +01:00
Adam Hathcock 6d54be55db Use App and Version when doing otel service name (#61) 2024-08-07 12:54:07 +01:00
Adam Hathcock 7148aa0a6f Refining OTEL/Logging configuration (#57)
* More defined configuration

* more config changes and fmt

* fix usages of app versions

* add endpoints and remove seq

* forgot to commit

* Fixes around tracing

* use more strong typing

* add test
2024-08-06 16:24:55 +01:00
Adam Hathcock f8e4682670 Reduce thread usage by removing a possible unneeded Task.Run (#52)
* Reduce thread usage by removing a possible unneeded Task.Run

* fmt
2024-08-05 09:13:10 +01:00
Claire Kuang 9da1d791cc Restructures proxies, removes layer colors, and adds color proxy class (#58) 2024-08-02 18:24:58 +03:00
Adam Hathcock 77ffcead69 Speckle.Logging with ILRepack (#54)
* Revert SQLite to allow side by side

* Update Serilog to 3.1.1 and remove Sentry

* fmt

* Revert serilog change

* first pass

* downgrade to MELA 2.2 to avoid conflicts

* add otel

* Speckle.Logging proper

* move otel and logging to make core dependant

* readd configuration

* revert namespace and add console tracing

* fmt

* Remove extra usings

* readd serilog

* fix deps

* remove extras for build

* some reversions

* add more context

* Fix conversion

* More explicit

* Revert naming

* back to public loggerfactory

* Remove more obsolete things

* Drop ME.Logging dependency and expose logging interface

* restore integration test compose

* fmt

* remove the ME Logging dependency
2024-07-31 15:37:10 +01:00
Claire Kuang a9a9b6b525 Merge pull request #56 from specklesystems/curve-encoding-bug
fix(Curve): null display value on curves decoded in breps
2024-07-31 11:52:47 +01:00
Claire Kuang adcf81b5c8 Merge branch 'curve-encoding-bug' of https://github.com/specklesystems/speckle-sharp-sdk into curve-encoding-bug 2024-07-31 11:48:07 +01:00
Claire Kuang fc46610b5b Update RevitInstance.cs 2024-07-31 11:47:57 +01:00
Claire Kuang 69fceb4d09 Merge branch 'dev' into curve-encoding-bug 2024-07-31 11:42:15 +01:00
Claire Kuang 9671745fdb fixes null display value on curves decoded in breps 2024-07-31 11:36:27 +01:00
Adam Hathcock 787ad92ff2 Remove extra usings (#55)
* Remove extra usings

* readd serilog
2024-07-30 11:07:52 +01:00
Adam Hathcock e79716c5f0 Remove Sentry (#49)
* Revert SQLite to allow side by side

* Update Serilog to 3.1.1 and remove Sentry

* fmt

* Revert serilog change
2024-07-29 16:10:26 +01:00
Jedd Morgan 1f92335d6d retry policy for server api (#53) 2024-07-26 13:37:28 +01:00
Adam Hathcock 02f75c717c Add NotNull Empty and tests (#51) 2024-07-24 15:05:44 +01:00
Claire Kuang ce3e591a47 feat: CNX-19 adds render material proxy class (#50)
* adds render material proxy class

* Update RenderMaterial.cs

* Update RenderMaterial.cs

* refactors IProxyCollection interface

* Update RenderMaterial.cs
2024-07-24 11:43:39 +00:00
Adam Hathcock 059253ff39 Revert SQLite to allow side by side (#48) 2024-07-24 11:38:34 +01:00
Adam Hathcock 957a284332 Change DI sig for Server and adjust helpers (#46)
* Change DI sig for Server and adjust helpers

* Remove V1 server transport
2024-07-23 12:35:27 +01:00
Adam Hathcock 5c7a96b960 ensure proper disposal of transports from di factory (#44)
* Rework servertransport to have proper interface and factory

* fmt

* rename class

* exclude factory from tests
2024-07-18 16:13:17 +01:00
Adam Hathcock 58911632e3 add xml docs and they get added to nugets (#45) 2024-07-18 10:05:39 +01:00
Adam Hathcock 9e2f5d57de Merge pull request #43 from specklesystems/add-local
add local config
2024-07-18 09:00:31 +01:00
Adam Hathcock 64267d7697 add local config 2024-07-18 08:32:33 +01:00
Adam Hathcock ee94ac2bf9 Merge pull request #42 from specklesystems/fix-gitversion
Fix gitversion
2024-07-17 15:50:53 +01:00
Claire Kuang 5f8379806d Merge pull request #40 from specklesystems/claire/collections-refactor
refactor(Collections): changes namespace of collections and adds interfaces
2024-07-17 15:09:11 +01:00
Oğuzhan Koral 580521f3b0 Add group proxy (#41) 2024-07-17 15:47:39 +02:00
Adam Hathcock 18ad5f5c11 do pack local 2024-07-17 14:44:21 +01:00
Adam Hathcock fcf4e223f7 version based on branch and more git locally 2024-07-17 14:42:59 +01:00
Claire Kuang a16bc4eae0 updates tests 2024-07-17 14:28:51 +01:00
Claire Kuang a15390ab0d Update Collection.cs 2024-07-17 14:22:37 +01:00
Claire Kuang a2f0270a6e adds old collections namespace deprecation 2024-07-17 14:22:27 +01:00
Claire Kuang 04446b9e79 changes namespace of collections and adds interfaces 2024-07-17 10:40:55 +01:00
Dimitrie Stefanescu f408980a98 Merge pull request #38 from specklesystems/oguzhan/remove-point-value-prop
Have it back
2024-07-17 10:08:55 +01:00
oguzhankoral b70a0c6fe3 Have it back 2024-07-17 11:08:05 +02:00
Dimitrie Stefanescu 7f50e9a09a Merge pull request #37 from specklesystems/oguzhan/remove-point-value-prop
Remove value prop from Point object
2024-07-17 10:01:40 +01:00
oguzhankoral b52d8e0023 Remove value prop from Point object 2024-07-17 11:00:31 +02:00
Oğuzhan Koral e97597596a Merge pull request #36 from specklesystems/oguzhan/fix-point-value-getter
Remove the Point value prop getter
2024-07-17 09:37:17 +02:00
oguzhankoral 4536ecee87 Remove the Point value prop getter 2024-07-17 09:32:05 +02:00
Dimitrie Stefanescu 72b7123f7b Merge pull request #35 from specklesystems/claire/layer-constructor-fix
Fixes Layer class constructors
2024-07-16 17:44:40 +01:00
Claire Kuang 72fe3d1384 Update Layer.cs 2024-07-16 17:38:42 +01:00
Dimitrie Stefanescu 481a095ed3 Merge pull request #34 from specklesystems/dim/dui3/layers
feat(dui3): adds layer concept in core as a poc
2024-07-16 15:03:08 +01:00
Dimitrie Stefanescu e3a5af037e chore: formatting 2024-07-16 14:56:28 +01:00
Dimitrie Stefanescu fe27951d96 feat(dui3): adds layer concept in core as a poc 2024-07-16 14:49:37 +01:00
Jedd Morgan baf5ab0b14 Fixed mistake (#32) 2024-07-16 10:47:56 +01:00
Adam Hathcock 057aef10c0 Merge pull request #31 from specklesystems/fixed-versions
Use GitVersion.MsBuild
2024-07-16 10:16:01 +01:00
Adam Hathcock c35ce07337 remove extra versioning stuff 2024-07-16 10:09:41 +01:00
Jedd Morgan e088e139af fix(core): Fixed regression with displayValue helper functions (#30) 2024-07-16 11:06:50 +02:00
Adam Hathcock dc62aa22b9 Use GitVersion.MsBuild 2024-07-16 10:06:29 +01:00
Adam Hathcock cd67efdfff Use the non-prerelease DoubleNumerics package (#29) 2024-07-16 08:15:28 +00:00
Dimitrie Stefanescu f0124a76c1 Merge pull request #28 from specklesystems/oguzhan/add-name-prop-to-definition
Add name property to definition proxy
2024-07-16 08:45:38 +01:00
oguzhankoral 5d4e14e802 Add name property to definition proxy 2024-07-16 09:28:14 +02:00
Adam Hathcock a88f860c5e Merge pull request #27 from specklesystems/git-version
switch from minver to gitversion
2024-07-15 15:16:35 +01:00
Adam Hathcock 1112ec39ab clean up tests and altcover the integration tests 2024-07-12 15:42:40 +01:00
Adam Hathcock 3ec1007b7b format 2024-07-12 15:21:06 +01:00
Adam Hathcock 82d51af262 fix checkout 2024-07-12 15:20:00 +01:00
Adam Hathcock e83c0125ba oops be linux 2024-07-12 15:17:24 +01:00
Adam Hathcock b23e91651c switch from minver to gitversion 2024-07-12 15:15:19 +01:00
Jedd Morgan db88dbe6d0 Avoid wrapping cancellation exception (#26) 2024-07-11 14:58:08 +01:00
Jedd Morgan da792173ff Added new Version Mutations (#25) 2024-07-11 14:03:02 +01:00
Jedd Morgan 51f0c26838 Re added level public setter for Revit Column (#24)
* Readded revit wall level setter

* csharpier

* Revit Column
2024-07-11 09:33:06 +01:00
Jedd Morgan cb14c43381 Readded revit wall level setter (#23) 2024-07-10 16:56:19 +00:00
Adam Hathcock ea68a245b8 Use a different minver id for now 2024-07-10 17:01:06 +01:00
Adam Hathcock c5df445483 git version 2024-07-10 16:50:37 +01:00
Adam Hathcock adadaa4d91 add dev to main build 2024-07-10 16:47:08 +01:00
Adam Hathcock 6e46fd2f8a use more build fun for nugets (#22)
* add license file

* use more build for nugets
2024-07-10 15:45:06 +00:00
Adam Hathcock 5df0c4401e Empty 2024-07-10 16:31:23 +01:00
Adam Hathcock 6778b7086c All int tests on CI: Don't run non-int tests during int tests. Reduce logging level (#20)
* Add Integration tests to CI (#19)

* Add Instances base (#6)

* Use Uri for checks in GetAccounts function (#8)

* Add integration and perf tests to sln (#9)

* Remove perf tests (#10)

* remove perf tests

* do all unit tests

* Code coverage (#11)

* code coverage

* enable codecov for GA

* Update README.md

* Update coverage and dependencies (#12)

* Update coverage and dependencies

* fmt

* add codecov config

* merge DUI3/Alpha into sdk (#13)

* merge DUI3/Alpha into sdk

* formatting

* Merge Objects dui3/alpha -> dev (#14)

* merge DUI3/Alpha into sdk

* formatting

* Objects changes

* Objects tests

* Unit test project

* add coverage exclusion

* fix some tests and fix nullability errors

* update codecov to be less intrusive (#15)

* update codecov to be less intrusive

* fix codecov yaml

* add coverage exclusion

* Merge sharp `dui3/alpha` -> sdk `main` (#16)

* Merge

* csharpier format

* Fixed polysharp issues

* Integration Tests

* Fixes

* add build for docker compose

* add integration tests

* remove extra services

* update healthcheck for server

---------

Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>
Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>

* Don't run non-int tests during int tests.  Reduce logging level

---------

Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>
Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2024-07-10 13:52:19 +01:00
Adam Hathcock 2dcf89f6fe Merge pull request #21 from specklesystems/main
Main to dev
2024-07-10 13:42:14 +01:00
Adam Hathcock 3249265560 Merge branch 'dev' 2024-07-09 13:58:09 +01:00
Adam Hathcock 200b84f49a Main to dev (#18)
* Add Instances base (#6)

* Use Uri for checks in GetAccounts function (#8)

* Add integration and perf tests to sln (#9)

* Remove perf tests (#10)

* remove perf tests

* do all unit tests

* Code coverage (#11)

* code coverage

* enable codecov for GA

* Update README.md

* Update coverage and dependencies (#12)

* Update coverage and dependencies

* fmt

* add codecov config

* merge DUI3/Alpha into sdk (#13)

* merge DUI3/Alpha into sdk

* formatting

* Merge Objects dui3/alpha -> dev (#14)

* merge DUI3/Alpha into sdk

* formatting

* Objects changes

* Objects tests

* Unit test project

* update codecov to be less intrusive (#15)

* update codecov to be less intrusive

* fix codecov yaml

* add coverage exclusion

* Merge sharp `dui3/alpha` -> sdk `main` (#16)

* Merge

* csharpier format

* Fixed polysharp issues

* Integration Tests

* Fixes

* Some nullability fixes (#17)

* add coverage exclusion

* fix some tests and fix nullability errors

---------

Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>
Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2024-07-09 13:56:03 +01:00
Jedd Morgan 1268a08baf Merge sharp dui3/alpha -> sdk main (#16)
* Merge

* csharpier format

* Fixed polysharp issues

* Integration Tests

* Fixes
2024-07-09 12:32:24 +01:00
Adam Hathcock 46c3f9d1ac update codecov to be less intrusive (#15)
* update codecov to be less intrusive

* fix codecov yaml

* add coverage exclusion
2024-07-09 12:52:53 +02:00
Jedd Morgan 29e7e84f4e Merge Objects dui3/alpha -> dev (#14)
* merge DUI3/Alpha into sdk

* formatting

* Objects changes

* Objects tests

* Unit test project
2024-07-08 16:30:54 +01:00
Jedd Morgan b2883c91e4 merge DUI3/Alpha into sdk (#13)
* merge DUI3/Alpha into sdk

* formatting
2024-07-08 14:03:54 +01:00
Adam Hathcock 82b7ca1a26 Update coverage and dependencies (#12)
* Update coverage and dependencies

* fmt

* add codecov config
2024-07-04 10:14:38 +01:00
Adam Hathcock b56eac2f50 Update README.md 2024-07-04 09:44:40 +01:00
Adam Hathcock f26f79c3c8 Code coverage (#11)
* code coverage

* enable codecov for GA
2024-07-04 09:43:16 +01:00
Adam Hathcock a0b723b2cb Remove perf tests (#10)
* remove perf tests

* do all unit tests
2024-07-04 09:34:57 +01:00
Adam Hathcock 1b472be6b7 Add integration and perf tests to sln (#9) 2024-07-02 18:24:44 +01:00
Oğuzhan Koral 662984268b Use Uri for checks in GetAccounts function (#8) 2024-07-02 15:00:55 +01:00
Adam Hathcock 1d681f9166 Add Instances base (#6) 2024-06-27 17:42:07 +01:00
Adam Hathcock cfe5f330c9 trying to get the deterministic builds checkbox on nuget again, works locally 2024-06-25 16:29:14 +01:00
Adam Hathcock 17e6780357 trying to get the deterministic builds checkbox on nuget (#4) 2024-06-25 16:00:18 +01:00
Adam Hathcock 98132d2811 Make name Civil3d instead of Civil (#3) 2024-06-25 15:29:24 +01:00
Adam Hathcock d9d1865504 downgrade sqlite to match core 2024-06-25 10:57:44 +01:00
Adam Hathcock 405f3f4ee4 renamespace and other fixes (#2)
* renamespace

* add to sln and move

* manage package centrally

* add sourcelink and use GlobalPackageReference

* properly use globals

* fix nuget push

* fix readme

* add namespace handling for new types

* Removing used classes to stop viral spread of dependencies.  Some JSON usage was STJ and not newtonsoft

* fmt

* initial test

* serialization namespace test

* fmt

* put back old namespaces

* fix tests

* fixes while trying to do a roundtrip test

* remove namespace fix
2024-06-25 10:27:59 +01:00
Adam Hathcock ca350002ba add packing and minver (#1)
* add packing and minver

* update permissions

* fix build script

* fix it again :(

* fmt

* update sqlite to avoid rid warning
2024-06-21 13:18:10 +01:00
Adam Hathcock 4ea9e4b9aa reformat tests 2024-06-21 12:38:25 +01:00
Adam Hathcock 2269182922 fmt 2024-06-21 12:33:55 +01:00
Adam Hathcock d7dbd3349f start build 2024-06-21 12:33:24 +01:00
Adam Hathcock ebec2c5bd2 add misc 2024-06-21 10:40:54 +01:00
Adam Hathcock a74ead9bd7 tests pass and everything compiles 2024-06-21 10:36:21 +01:00
Adam Hathcock f46aa4f50b compiles and tests pass 2024-06-21 10:23:34 +01:00
Adam Hathcock afde379cda trying to move core 2024-06-21 08:42:42 +01:00
449 changed files with 39450 additions and 2 deletions
+20
View File
@@ -0,0 +1,20 @@
{
"version": 1,
"isRoot": true,
"tools": {
"csharpier": {
"version": "1.0.1",
"commands": [
"csharpier"
],
"rollForward": false
},
"gitversion.tool": {
"version": "6.1.0",
"commands": [
"dotnet-gitversion"
],
"rollForward": false
}
}
}
+26
View File
@@ -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.*
+7
View File
@@ -0,0 +1,7 @@
printWidth: 120
useTabs: false
indentSize: 2
preprocessorSymbolSets:
- ""
- "DEBUG"
- "DEBUG,CODE_STYLE"
+327
View File
@@ -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
+8
View File
@@ -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
+76
View File
@@ -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
+58
View File
@@ -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).
🙌❤️💙💚💜🙌
+6
View File
@@ -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
+32
View File
@@ -0,0 +1,32 @@
name: .NET CI Build
on:
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- 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') }}
- name: 🔫 Build All
run: ./build.sh
- name: Upload coverage reports to Codecov with GitHub Action
uses: codecov/codecov-action@v5
with:
files: tests/**/coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
+37
View File
@@ -0,0 +1,37 @@
name: .NET Build and Publish
on:
push:
branches: ["main", "dev"]
tags: ["3.*"]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- 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') }}
- name: 🔫 Build and Pack
run: ./build.sh pack
- 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
+23
View File
@@ -0,0 +1,23 @@
**/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.*
+50
View File
@@ -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).
🙌❤️💙💚💜🙌
+4
View File
@@ -0,0 +1,4 @@
CA1502: 25
CA1501: 5
CA1506(Method): 50
CA1506(Type): 95
+73
View File
@@ -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>
+17
View File
@@ -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>
+39
View File
@@ -0,0 +1,39 @@
<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="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.Threading.Channels" Version="9.0.4" />
<PackageVersion Include="Verify.Quibble" Version="2.1.1" />
<PackageVersion Include="Verify.Xunit" Version="29.4.0" />
<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>
+6
View File
@@ -0,0 +1,6 @@
workflow: GitFlow/v1
next-version: 3.0.0
branches:
main:
prevent-increment:
when-current-commit-tagged: true
+17
View File
@@ -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!
+201
View File
@@ -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.
+76 -2
View File
@@ -1,2 +1,76 @@
# speckle-sharp-sdk
The speckle core
![Speckle Box](/logo.png)
Speckle | Sharp | SDK
=================================================================================================================================
[![Twitter Follow](https://img.shields.io/twitter/follow/SpeckleSystems?style=social)](https://twitter.com/SpeckleSystems) [![Community forum users](https://img.shields.io/discourse/users?server=https%3A%2F%2Fspeckle.community&style=flat-square&logo=discourse&logoColor=white)](https://speckle.community) [![website](https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square)](https://speckle.systems) [![docs](https://img.shields.io/badge/docs-speckle.guide-orange?style=flat-square&logo=read-the-docs&logoColor=white)](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
[![codecov](https://codecov.io/gh/specklesystems/speckle-sharp-sdk/branch/dev/graph/badge.svg?token=TTM5OGr38m)](https://codecov.io/gh/specklesystems/speckle-sharp-sdk)
> [!WARNING]
> This is an early beta release, not meant for use in production! We're working to stabilise the 3.0 API, and until then there will be breaking changes. You have been warned!
# 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): Transports, serialization, API wrappers, and logging.
- [`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 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 Speckle next generation repositories:
- [`speckle-sharp-connectors`](https://github.com/specklesystems/speckle-sharp-connectors): our csharp repo of next gen connectors
- [`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
Make sure you clone this repository together with its submodules: `git clone https://github.com/specklesystems/speckle-sharp-sdk.git -recursive`.
Afterwards, just restore all the NuGet packages and hit Build!
### Developing
This project is evolving fast, to better understand how to use Core we suggest checking out the Unit and Integration tests. Running the integration tests locally requires a local server running on your computer.
We'll be also adding [preliminary documentation on our forum](https://discourse.speckle.works/c/speckle-insider/10).
### Tests
There are two test projects, one for unit tests and one for integration tests. The latter needs a server running locally in order to run.
## 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).
+123
View File
@@ -0,0 +1,123 @@
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}"
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
GitVersion.yml = GitVersion.yml
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
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
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}
EndGlobalSection
EndGlobal
+3
View File
@@ -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>
+3
View File
@@ -0,0 +1,3 @@
$ErrorActionPreference = "Stop";
dotnet run --project build/build.csproj -- $args
Executable
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail
dotnet run --project build/build.csproj -- "$@"
+187
View File
@@ -0,0 +1,187 @@
using System.Text.Json;
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 async Task<(string, string)> GetVersions()
{
var (output, _) = await ReadAsync("dotnet", "dotnet-gitversion /output json").ConfigureAwait(false);
output = output.Trim();
var jDoc = JsonDocument.Parse(output);
var version = jDoc.RootElement.GetProperty("FullSemVer").GetString() ?? "3.0.0-localBuild";
var fileVersion = jDoc.RootElement.GetProperty("AssemblySemFileVer").GetString() ?? "3.0.0.0";
return (version, 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) = await GetVersions().ConfigureAwait(false);
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) = await GetVersions().ConfigureAwait(false);
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);
+11
View File
@@ -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>
+57
View File
@@ -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
View File
@@ -0,0 +1,13 @@
coverage:
status:
project:
default:
informational: true
target: auto
threshold: 1%
base: auto
patch:
default:
informational: true
github_checks:
annotations: false
+111
View File
@@ -0,0 +1,111 @@
version: "3.9"
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: "redis:6.0-alpine"
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
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(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_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"
networks:
default:
name: speckle-server
volumes:
postgres-data:
redis-data:
minio-data:
+6
View File
@@ -0,0 +1,6 @@
{
"sdk": {
"version": "8.0.400",
"rollForward": "latestMinor"
}
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

+356
View File
@@ -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
+10
View File
@@ -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
+69
View File
@@ -0,0 +1,69 @@
using Speckle.Objects.Geometry;
using Speckle.Sdk.Models;
using Point = Speckle.Objects.Geometry.Point;
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>
/// Origin point, relation to the text is defined by AlignmentHorizontal and AlignmentVertical
/// </summary>
public required Point origin { 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>
/// 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 will be null if the text object orientation follows camera view
/// </summary>
public 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,
}
+16
View File
@@ -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;
}
+27
View File
@@ -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;
}
+16
View File
@@ -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;
}
+24
View File
@@ -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;
}
+36
View File
@@ -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;
}
+24
View File
@@ -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;
}
}
+165
View File
@@ -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,
}
+57
View File
@@ -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;
}
+740
View File
@@ -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,
}
+36
View File
@@ -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];
}
+28
View File
@@ -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();
}
+24
View File
@@ -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();
}
+38
View File
@@ -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];
}
+33
View File
@@ -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;
+82
View File
@@ -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;
}
}
+190
View File
@@ -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;
}
}
+76
View File
@@ -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;
}
}
+122
View File
@@ -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);
}
}
}
+169
View File
@@ -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]);
}
}
+103
View File
@@ -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;
}
}
+247
View File
@@ -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;
}
}
+159
View File
@@ -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;
}
}
+124
View File
@@ -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];
}
}
}
+97
View File
@@ -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;
}
}
+42
View File
@@ -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; }
}
+255
View File
@@ -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),
};
}
}
+216
View File
@@ -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;
}
}
}
+182
View File
@@ -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
+23
View File
@@ -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,58 @@
using System.Drawing;
using Speckle.Newtonsoft.Json;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Proxies;
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();
}
}
/// <summary>
/// Used to store render material to object relationships in root collections
/// </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; }
}
+135
View File
@@ -0,0 +1,135 @@
using Speckle.DoubleNumerics;
using Speckle.Sdk.Common;
using Speckle.Sdk.Models;
namespace Speckle.Objects.Other;
/// <summary>
/// Generic transform class
/// </summary>
[SpeckleType("Objects.Other.Transform")]
public class Transform : Base
{
/// <summary>
/// The column-based 4x4 transform matrix
/// </summary>
/// <remarks>
/// Graphics based apps typically use column-based matrices, where the last column defines translation.
/// Modelling apps may use row-based matrices, where the last row defines translation. Transpose if so.
/// </remarks>
public required Matrix4x4 matrix { get; set; } = Matrix4x4.Identity;
/// <summary>
/// Units for translation
/// </summary>
public required string units { get; set; }
/// <summary>
/// Converts this transform to the input units
/// </summary>
/// <param name="newUnits">The target units</param>
/// <returns>A matrix array with the translation scaled by input units</returns>
/// <remarks>If either the transform's <see cref="units"/> or the given <paramref name="newUnits"/> is <see langword="null"/>, will return the matrix array data unscaled</remarks>
public double[] ConvertToUnits(string newUnits)
{
if (newUnits == null || units == null)
{
return ToArray();
}
var sf = Units.GetConversionFactor(units, newUnits);
return new[]
{
matrix.M11,
matrix.M12,
matrix.M13,
matrix.M14 * sf,
matrix.M21,
matrix.M22,
matrix.M23,
matrix.M24 * sf,
matrix.M31,
matrix.M32,
matrix.M33,
matrix.M34 * sf,
matrix.M41,
matrix.M42,
matrix.M43,
matrix.M44,
};
}
// Creates a matrix4x4 from a double array
internal static Matrix4x4 CreateMatrix(double[] value)
{
return new Matrix4x4(
value[0],
value[1],
value[2],
value[3],
value[4],
value[5],
value[6],
value[7],
value[8],
value[9],
value[10],
value[11],
value[12],
value[13],
value[14],
value[15]
);
}
// Creates a matrix from a float array
internal static Matrix4x4 CreateMatrix(float[] value)
{
return new Matrix4x4(
Convert.ToDouble(value[0]),
Convert.ToDouble(value[1]),
Convert.ToDouble(value[2]),
Convert.ToDouble(value[3]),
Convert.ToDouble(value[4]),
Convert.ToDouble(value[5]),
Convert.ToDouble(value[6]),
Convert.ToDouble(value[7]),
Convert.ToDouble(value[8]),
Convert.ToDouble(value[9]),
Convert.ToDouble(value[10]),
Convert.ToDouble(value[11]),
Convert.ToDouble(value[12]),
Convert.ToDouble(value[13]),
Convert.ToDouble(value[14]),
Convert.ToDouble(value[15])
);
}
/// <summary>
/// Returns the double array of the transform matrix
/// </summary>
/// <returns></returns>
public double[] ToArray()
{
return new double[]
{
matrix.M11,
matrix.M12,
matrix.M13,
matrix.M14,
matrix.M21,
matrix.M22,
matrix.M23,
matrix.M24,
matrix.M31,
matrix.M32,
matrix.M33,
matrix.M34,
matrix.M41,
matrix.M42,
matrix.M43,
matrix.M44,
};
}
}
+21
View File
@@ -0,0 +1,21 @@
using Speckle.Newtonsoft.Json;
using Speckle.Sdk.Models;
namespace Speckle.Objects.Primitive;
[SpeckleType("Objects.Primitive.Interval")]
public class Interval : Base
{
public required double start { get; set; }
public required double end { get; set; }
[JsonIgnore]
public double Length => Math.Abs((end) - (start));
public override string ToString()
{
return base.ToString() + $"[{start}, {end}]";
}
public static Interval UnitInterval => new() { start = 0, end = 1 };
}
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Compiler Properties">
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
<PolySharpExcludeGeneratedTypes>System.Runtime.CompilerServices.RequiresLocationAttribute</PolySharpExcludeGeneratedTypes>
<Configurations>Debug;Release;Local</Configurations>
</PropertyGroup>
<PropertyGroup Label="Nugetspec Package Properties">
<PackageId>Speckle.Objects</PackageId>
<Description>Objects is the default object model for Speckle</Description>
<PackageTags>$(PackageTags) objects</PackageTags>
</PropertyGroup>
<PropertyGroup Label="Nuget Package Properties">
<IsPackable>true</IsPackable>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<PropertyGroup Label="Analyers">
<NoWarn>
$(NoWarn);
CA1819;CA1008;CA2225;
</NoWarn>
</PropertyGroup>
<ItemGroup Label="Expose internals to test projects">
<InternalsVisibleTo Include="Speckle.Objects.Tests.Unit" />
</ItemGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\Speckle.Sdk\Speckle.Sdk.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,271 @@
using System.Runtime.CompilerServices;
using Speckle.Objects.Geometry;
namespace Speckle.Objects.Utils;
/// <summary>
/// Set of functions to triangulate n-gon faces (i.e. polygon faces with an arbitrary (n) number of vertices) in <see cref="Mesh"/>es.
/// </summary>
public static class MeshTriangulationHelper
{
/// <summary>
/// Triangulates all faces in <paramref name="mesh"/>.
/// </summary>
/// <param name="mesh">The mesh to triangulate.</param>
/// <param name="preserveQuads">If <see langword="true"/>, will not triangulate quad faces.</param>
public static void TriangulateMesh(this Mesh mesh, bool preserveQuads = false)
{
List<int> triangles = new(mesh.faces.Count); //Our new list is going to be at least as big as our old one
int i = 0;
while (i < mesh.faces.Count)
{
int n = mesh.faces[i];
if (n < 3)
{
n += 3; // 0 -> 3, 1 -> 4
}
if (n == 3)
{
triangles.Add(3);
triangles.Add(mesh.faces[i + 1]);
triangles.Add(mesh.faces[i + 2]);
triangles.Add(mesh.faces[i + 3]);
}
else if (preserveQuads && n == 4)
{
triangles.Add(4);
triangles.Add(mesh.faces[i + 1]);
triangles.Add(mesh.faces[i + 2]);
triangles.Add(mesh.faces[i + 3]);
triangles.Add(mesh.faces[i + 4]);
}
else
{
var triangle = TriangulateFace(i, mesh);
triangles.AddRange(triangle);
}
i += n + 1;
}
mesh.faces = triangles;
}
/// <overloads>Overload using a <see cref="Mesh"/>, does not mutate <paramref name="mesh"/></overloads>
/// <inheritdoc cref="TriangulateFace(int,IReadOnlyList{int},IReadOnlyList{double},bool)"/>
public static List<int> TriangulateFace(int faceIndex, Mesh mesh, bool includeIndicators = true)
{
return TriangulateFace(faceIndex, mesh.faces, mesh.vertices, includeIndicators);
}
/// <summary>
/// Calculates the triangulation of the face at <paramref name="faceIndex"/> in <paramref name="faces"/> list.
/// </summary>
/// <remarks>
/// This implementation is based the ear clipping method
/// Proposed by "Christer Ericson (2005) <i>Real-Time Collision Detection</i>".
/// </remarks>
/// <param name="faceIndex">The index of the face's cardinality indicator <c>n</c> in <paramref name="faces"/> list</param>.
/// <param name="faces"></param>
/// <param name="vertices"></param>
/// <param name="includeIndicators">if <see langword="true"/>, the returned list will include cardinality indicators for each triangle
/// (i.e 4 ints for each tri), otherwise will simply be 3 ints for each tri.</param>
/// <returns>List of triangle faces in the specified format.</returns>
public static List<int> TriangulateFace(
int faceIndex,
IReadOnlyList<int> faces,
IReadOnlyList<double> vertices,
bool includeIndicators = true
)
{
int n = faces[faceIndex];
if (n < 3)
{
n += 3; // 0 -> 3, 1 -> 4
}
#region Local Funcitions
//Converts from relative to absolute index (returns index in mesh.vertices list)
int AsIndex(int v) => faceIndex + v + 1;
//Gets vertex from a relative vert index
Vector3 V(int v)
{
int index = faces[AsIndex(v)] * 3;
return new Vector3(vertices[index], vertices[index + 1], vertices[index + 2]);
}
#endregion
int intsPerTri = includeIndicators ? 4 : 3;
List<int> triangleFaces = new((n - 2) * intsPerTri);
//Calculate face normal using the Newell Method
Vector3 faceNormal = Vector3.Zero;
for (int ii = n - 1, jj = 0; jj < n; ii = jj, jj++)
{
Vector3 iPos = V(ii);
Vector3 jPos = V(jj);
faceNormal.x += (jPos.y - iPos.y) * (iPos.z + jPos.z); // projection on yz
faceNormal.y += (jPos.z - iPos.z) * (iPos.x + jPos.x); // projection on xz
faceNormal.z += (jPos.x - iPos.x) * (iPos.y + jPos.y); // projection on xy
}
faceNormal.Normalize();
//Set up previous and next links to effectively form a double-linked vertex list
int[] prev = new int[n],
next = new int[n];
for (int j = 0; j < n; j++)
{
prev[j] = j - 1;
next[j] = j + 1;
}
prev[0] = n - 1;
next[n - 1] = 0;
//Start clipping ears until we are left with a triangle
int i = 0;
int counter = 0;
while (n >= 3)
{
bool isEar = true;
//If we are the last triangle or we have exhausted our vertices, the below statement will be false
if (n > 3 && counter < n)
{
Vector3 prevVertex = V(prev[i]);
Vector3 earVertex = V(i);
Vector3 nextVertex = V(next[i]);
if (TriangleIsCCW(faceNormal, prevVertex, earVertex, nextVertex))
{
int k = next[next[i]];
do
{
if (TestPointTriangle(V(k), prevVertex, earVertex, nextVertex))
{
isEar = false;
break;
}
k = next[k];
} while (k != prev[i]);
}
else
{
isEar = false;
}
}
if (isEar)
{
int a = faces[AsIndex(i)];
int b = faces[AsIndex(next[i])];
int c = faces[AsIndex(prev[i])];
if (includeIndicators)
{
triangleFaces.Add(3);
}
triangleFaces.Add(a);
triangleFaces.Add(b);
triangleFaces.Add(c);
next[prev[i]] = next[i];
prev[next[i]] = prev[i];
n--;
i = prev[i];
counter = 0;
}
else
{
i = next[i];
counter++;
}
}
return triangleFaces;
}
/// <summary>
/// Tests if point <paramref name="v"/> is within triangle <paramref name="a"/><paramref name="b"/><paramref name="c"/>
/// </summary>
/// <returns>true if <paramref name="v"/> is within triangle</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TestPointTriangle(Vector3 v, Vector3 a, Vector3 b, Vector3 c)
{
static bool Test(Vector3 v, Vector3 a, Vector3 b)
{
Vector3 crossA = v.Cross(a);
Vector3 crossB = a.Cross(b);
double dotWithEpsilon = double.Epsilon + crossA.Dot(crossB);
return Math.Sign(dotWithEpsilon) != -1;
}
return Test(b - a, v - a, c - a) && Test(c - b, v - b, a - b) && Test(a - c, v - c, b - c);
}
/// <summary>
/// Checks that triangle <paramref name="a"/><paramref name="b"/><paramref name="c"/> is clockwise with reference to <paramref name="referenceNormal"/>
/// </summary>
/// <param name="referenceNormal">The normal direction of the face</param>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="c"></param>
/// <returns>true if triangle is ccw</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TriangleIsCCW(Vector3 referenceNormal, Vector3 a, Vector3 b, Vector3 c)
{
Vector3 triangleNormal = (c - a).Cross(b - a);
triangleNormal.Normalize();
return referenceNormal.Dot(triangleNormal) > 0.0f;
}
/// <summary>
/// 3-dimension x, Y, Z Vector of <see cref="double"/>s encapsulating necessary vector mathematics
/// </summary>
private struct Vector3
{
public double x,
y,
z;
public Vector3(double x, double y, double z)
{
this.x = x;
this.y = y;
this.z = z;
}
public static readonly Vector3 Zero = new(0, 0, 0);
public static Vector3 operator +(Vector3 a, Vector3 b) => new(a.x + b.x, a.y + b.y, a.z + b.z);
public static Vector3 operator -(Vector3 a, Vector3 b) => new(a.x - b.x, a.y - b.y, a.z - b.z);
public readonly double Dot(Vector3 v)
{
return x * v.x + y * v.y + z * v.z;
}
public readonly Vector3 Cross(Vector3 v)
{
var x = this.y * v.z - this.z * v.y;
var y = this.z * v.x - this.x * v.z;
var z = this.x * v.y - this.y * v.x;
return new Vector3(x, y, z);
}
public readonly double SquareSum => x * x + y * y + z * z;
public void Normalize()
{
double scale = 1d / Math.Sqrt(SquareSum);
x *= scale;
y *= scale;
z *= scale;
}
}
}
+546
View File
@@ -0,0 +1,546 @@
{
"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"
}
},
"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=="
},
"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=="
},
"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.4.0",
"contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw=="
},
"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==",
"dependencies": {
"System.Buffers": "4.4.0",
"System.Numerics.Vectors": "4.4.0",
"System.Runtime.CompilerServices.Unsafe": "4.5.2"
}
},
"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": "4.5.3",
"contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw=="
},
"System.Runtime.InteropServices.WindowsRuntime": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "J4GUi3xZQLUBasNwZnjrffN8i5wpHrBtZoLG+OhRyGo/+YunMRWWtwoMDlUAIdmX0uRfpHIBDSV6zyr3yf00TA==",
"dependencies": {
"System.Runtime": "4.3.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.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": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"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"
}
},
"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=="
},
"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=="
},
"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.sdk": {
"type": "Project",
"dependencies": {
"GraphQL.Client": "[6.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.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=="
}
}
}
}
@@ -0,0 +1,44 @@
using System.Collections.Frozen;
namespace Speckle.Sdk.Dependencies;
public static class Collections
{
#if NET5_0_OR_GREATER
public static IReadOnlySet<T> Freeze<T>(this IEnumerable<T> source)
#else
public static IReadOnlyCollection<T> Freeze<T>(this IEnumerable<T> source)
#endif
{
return source.ToFrozenSet();
}
public static IReadOnlyDictionary<TKey, TValue> Freeze<TKey, TValue>(
this IEnumerable<KeyValuePair<TKey, TValue>> source
)
where TKey : notnull => source.ToFrozenDictionary();
}
public static class EnumerableExtensions
{
public static IEnumerable<int> RangeFrom(int from, int to) => Enumerable.Range(from, to - from + 1);
#if NETSTANDARD2_0
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector
)
{
var keys = new HashSet<TKey>();
foreach (var element in source)
{
if (keys.Contains(keySelector(element)))
{
continue;
}
keys.Add(keySelector(element));
yield return element;
}
}
#endif
}
@@ -0,0 +1,27 @@
using Polly;
using Polly.Contrib.WaitAndRetry;
namespace Speckle.Sdk.Dependencies;
public static class GraphQLRetry
{
public static async Task<T> ExecuteAsync<T, TInnerException>(
Func<Task<T>> func,
Action<Exception, TimeSpan>? onRetry = null
)
where TInnerException : Exception
{
var delay = Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromSeconds(1), 5);
var graphqlRetry = Policy
.HandleInner<TInnerException>()
.WaitAndRetryAsync(
delay,
(ex, timeout, _) =>
{
onRetry?.Invoke(ex, timeout);
}
);
return await graphqlRetry.ExecuteAsync(func).ConfigureAwait(false);
}
}
@@ -0,0 +1,23 @@
namespace Speckle.Sdk.Logging;
public interface ISdkActivity : IDisposable
{
void SetTag(string key, object? value);
void RecordException(Exception e);
string TraceId { get; }
void SetStatus(SdkActivityStatusCode code);
void InjectHeaders(Action<string, string> header);
}
public enum SdkActivityStatusCode
{
/// <summary>Unset status code is the default value indicating the status code is not initialized.</summary>
Unset,
/// <summary>Status code indicating the operation has been validated and completed successfully.</summary>
Ok,
/// <summary>Status code indicating an error is encountered during the operation.</summary>
Error,
}
@@ -0,0 +1,8 @@
using System.Runtime.CompilerServices;
namespace Speckle.Sdk.Logging;
public interface ISdkActivityFactory : IDisposable
{
ISdkActivity? Start(string? name = default, [CallerMemberName] string source = "");
}
+18
View File
@@ -0,0 +1,18 @@
using Microsoft.Extensions.ObjectPool;
namespace Speckle.Sdk.Dependencies;
public class Pool<T>
where T : class, new()
{
private readonly ObjectPool<T> _objectPool;
internal Pool(IPooledObjectPolicy<T> objectPolicy)
{
_objectPool = ObjectPool.Create(objectPolicy);
}
public T Get() => _objectPool.Get();
public void Return(T obj) => _objectPool.Return(obj);
}
+70
View File
@@ -0,0 +1,70 @@
using System.Collections.Concurrent;
using System.Text;
using Microsoft.Extensions.ObjectPool;
namespace Speckle.Sdk.Dependencies;
public static class Pools
{
public const int DefaultCapacity = 50;
public static Pool<Dictionary<string, object?>> ObjectDictionaries { get; } = new(new ObjectDictionaryPolicy());
private sealed class ObjectDictionaryPolicy : IPooledObjectPolicy<Dictionary<string, object?>>
{
public Dictionary<string, object?> Create() => new(50, StringComparer.OrdinalIgnoreCase);
public bool Return(Dictionary<string, object?> obj)
{
obj.Clear();
return true;
}
}
public static Pool<StringBuilder> StringBuilders { get; } =
new(new StringBuilderPooledObjectPolicy() { MaximumRetainedCapacity = 100 * 1024 * 1024 });
private sealed class ObjectDictionaryPolicy<TKey, TValue> : IPooledObjectPolicy<Dictionary<TKey, TValue>>
where TKey : notnull
{
public Dictionary<TKey, TValue> Create() => new(DefaultCapacity);
public bool Return(Dictionary<TKey, TValue> obj)
{
obj.Clear();
return true;
}
}
private sealed class ObjectConcurrentDictionaryPolicy<TKey, TValue>
: IPooledObjectPolicy<ConcurrentDictionary<TKey, TValue>>
where TKey : notnull
{
public ConcurrentDictionary<TKey, TValue> Create() => new(Environment.ProcessorCount, DefaultCapacity);
public bool Return(ConcurrentDictionary<TKey, TValue> obj)
{
obj.Clear();
return true;
}
}
private sealed class ObjectListPolicy<T> : IPooledObjectPolicy<List<T>>
{
public List<T> Create() => new(DefaultCapacity);
public bool Return(List<T> obj)
{
obj.Clear();
return true;
}
}
public static Pool<List<T>> CreateListPool<T>() => new(new ObjectListPolicy<T>());
public static Pool<Dictionary<TKey, TValue>> CreateDictionaryPool<TKey, TValue>()
where TKey : notnull => new(new ObjectDictionaryPolicy<TKey, TValue>());
public static Pool<ConcurrentDictionary<TKey, TValue>> CreateConcurrentDictionaryPool<TKey, TValue>()
where TKey : notnull => new(new ObjectConcurrentDictionaryPolicy<TKey, TValue>());
}
@@ -0,0 +1,33 @@
using System.Buffers;
using Speckle.Sdk.Dependencies;
namespace Speckle.Sdk.Serialisation.V2.Send;
public sealed class Batch<T> : IMemoryOwner<T>
where T : IHasByteSize
{
private static readonly Pool<List<T>> _pool = Pools.CreateListPool<T>();
#pragma warning disable IDE0032
private readonly List<T> _items = _pool.Get();
private int _batchByteSize;
#pragma warning restore IDE0032
public void Add(T item)
{
_items.Add(item);
_batchByteSize += item.ByteSize;
}
public void TrimExcess()
{
_items.TrimExcess();
_batchByteSize = _items.Sum(x => x.ByteSize);
}
public int BatchByteSize => _batchByteSize;
public List<T> Items => _items;
public void Dispose() => _pool.Return(_items);
public Memory<T> Memory => new(_items.ToArray());
}
@@ -0,0 +1,34 @@
using System.Buffers;
namespace Speckle.Sdk.Serialisation.V2.Send;
public static class BatchExtensions
{
public static Batch<T> CreateBatch<T>()
where T : IHasByteSize => new();
public static void TrimBatch<T>(ref IMemoryOwner<T> batch, bool isVerifiedFull)
where T : IHasByteSize
{
if (!isVerifiedFull)
{
((Batch<T>)batch).TrimExcess();
}
}
public static void AddBatchItem<T>(this IMemoryOwner<T> batch, T item)
where T : IHasByteSize => ((Batch<T>)batch).Add(item);
public static int GetBatchSize<T>(this IMemoryOwner<T> batch, int maxBatchSize)
where T : IHasByteSize
{
var currentSize = ((Batch<T>)batch).BatchByteSize;
if (currentSize > maxBatchSize)
{
//doing this to say it's full since the channel reader only does full being equivalent
return maxBatchSize;
}
return currentSize;
}
}
@@ -0,0 +1,22 @@
using System.Buffers;
using System.Threading.Channels;
using Open.ChannelExtensions;
namespace Speckle.Sdk.Serialisation.V2.Send;
public static class ChannelExtensions
{
public static BatchingChannelReader<T, IMemoryOwner<T>> BatchByByteSize<T>(
this ChannelReader<T> source,
int batchSize,
bool singleReader = false,
bool allowSynchronousContinuations = false
)
where T : IHasByteSize =>
new SizeBatchingChannelReader<T>(
source ?? throw new ArgumentNullException(nameof(source)),
batchSize,
singleReader,
allowSynchronousContinuations
);
}
@@ -0,0 +1,114 @@
using System.Threading.Channels;
using Open.ChannelExtensions;
namespace Speckle.Sdk.Dependencies.Serialization;
public abstract class ChannelLoader<T>(CancellationToken cancellationToken)
{
private const int RECEIVE_CAPACITY = 5000;
private const int HTTP_GET_CHUNK_SIZE = 500;
private const int MAX_PARALLELISM_HTTP = 4;
private static readonly TimeSpan HTTP_BATCH_TIMEOUT = TimeSpan.FromSeconds(2);
private const int MAX_SAVE_CACHE_BATCH = 500;
private const int MAX_SAVE_CACHE_PARALLELISM = 4;
private readonly CancellationTokenSource _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
private readonly Channel<string> _channel = Channel.CreateBounded<string>(
new BoundedChannelOptions(RECEIVE_CAPACITY)
{
AllowSynchronousContinuations = true,
Capacity = RECEIVE_CAPACITY,
SingleWriter = false,
SingleReader = false,
FullMode = BoundedChannelFullMode.Wait,
},
_ => throw new NotImplementedException("Dropping items not supported.")
);
protected async Task GetAndCache(IEnumerable<string> allChildrenIds, int? maxParallelism = null) =>
await _channel
.Source(allChildrenIds, _cts.Token)
.Pipe(maxParallelism ?? Environment.ProcessorCount, CheckCache, cancellationToken: _cts.Token)
.Filter(x => x is not null)
.Batch(HTTP_GET_CHUNK_SIZE)
.WithTimeout(HTTP_BATCH_TIMEOUT)
.PipeAsync(
maxParallelism ?? MAX_PARALLELISM_HTTP,
async x => await Download(x).ConfigureAwait(false),
-1,
false,
_cts.Token
)
.Join()
.Batch(MAX_SAVE_CACHE_BATCH)
.WithTimeout(HTTP_BATCH_TIMEOUT)
.ReadAllConcurrently(maxParallelism ?? MAX_SAVE_CACHE_PARALLELISM, SaveToCache, _cts.Token)
.ContinueWith(
t =>
{
Exception? ex = t.Exception;
if (ex is null && t.Status is TaskStatus.Canceled && !_cts.Token.IsCancellationRequested)
{
ex = new OperationCanceledException();
}
if (ex is not null)
{
RecordException(ex);
}
_channel.Writer.TryComplete(ex);
},
_cts.Token,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Current
)
.ConfigureAwait(false);
public abstract string? CheckCache(string id);
public async Task<List<T>> Download(List<string?> ids)
{
try
{
return await DownloadInternal(ids).ConfigureAwait(false);
}
#pragma warning disable CA1031
catch (Exception ex)
#pragma warning restore CA1031
{
RecordException(ex);
return [];
}
}
protected abstract Task<List<T>> DownloadInternal(List<string?> batch);
public void SaveToCache(List<T> batch)
{
try
{
SaveToCacheInternal(batch);
}
#pragma warning disable CA1031
catch (Exception ex)
#pragma warning restore CA1031
{
RecordException(ex);
}
}
protected abstract void SaveToCacheInternal(List<T> batch);
protected Exception? Exception { get; private set; }
private void RecordException(Exception ex)
{
Exception = ex;
_channel.Writer.TryComplete(ex);
//cancel everything!
_cts.Cancel();
}
}
@@ -0,0 +1,119 @@
using System.Buffers;
using System.Threading.Channels;
using Open.ChannelExtensions;
using Speckle.Sdk.Serialisation.V2.Send;
namespace Speckle.Sdk.Dependencies.Serialization;
public abstract class ChannelSaver<T>
where T : IHasByteSize
{
private const int SEND_CAPACITY = 1000;
private const int HTTP_SEND_CHUNK_SIZE = 25_000_000; //bytes
private static readonly TimeSpan HTTP_BATCH_TIMEOUT = TimeSpan.FromSeconds(2);
private const int MAX_PARALLELISM_HTTP = 4;
private const int HTTP_CAPACITY = 500;
private const int MAX_CACHE_WRITE_PARALLELISM = 4;
private const int MAX_CACHE_BATCH = 500;
private readonly Channel<T> _checkCacheChannel = Channel.CreateBounded<T>(
new BoundedChannelOptions(SEND_CAPACITY)
{
AllowSynchronousContinuations = true,
Capacity = SEND_CAPACITY,
SingleWriter = false,
SingleReader = false,
FullMode = BoundedChannelFullMode.Wait,
},
_ => throw new NotImplementedException("Dropping items not supported.")
);
public Task Start(
int? maxParallelism,
int? httpBatchSize,
int? cacheBatchSize,
CancellationToken cancellationToken
) =>
_checkCacheChannel
.Reader.BatchByByteSize(httpBatchSize ?? HTTP_SEND_CHUNK_SIZE)
.WithTimeout(HTTP_BATCH_TIMEOUT)
.PipeAsync(
maxParallelism ?? MAX_PARALLELISM_HTTP,
async x => await SendToServer(x).ConfigureAwait(false),
HTTP_CAPACITY,
false,
cancellationToken
)
.Join()
.Batch(cacheBatchSize ?? MAX_CACHE_BATCH)
.WithTimeout(HTTP_BATCH_TIMEOUT)
.ReadAllConcurrently(maxParallelism ?? MAX_CACHE_WRITE_PARALLELISM, SaveToCache, cancellationToken)
.ContinueWith(
t =>
{
Exception? ex = t.Exception;
if (ex is null && t.Status is TaskStatus.Canceled && !cancellationToken.IsCancellationRequested)
{
ex = new OperationCanceledException();
}
if (ex is not null)
{
RecordException(ex);
}
_checkCacheChannel.Writer.TryComplete(ex);
},
cancellationToken,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Current
);
public async Task SaveAsync(T item, CancellationToken cancellationToken)
{
if (Exception is not null)
{
return; //don't save if we're already done through an error
}
//can switch to check then try pattern when back pressure is needed or exceptions are too much
//the trees don't need to respond to back pressure
await _checkCacheChannel.Writer.WriteAsync(item, cancellationToken).ConfigureAwait(false);
}
private async Task<IMemoryOwner<T>> SendToServer(IMemoryOwner<T> batch)
{
try
{
await SendToServerInternal((Batch<T>)batch).ConfigureAwait(false);
return batch;
}
#pragma warning disable CA1031
catch (Exception ex)
#pragma warning restore CA1031
{
RecordException(ex);
return batch;
}
}
protected abstract Task SendToServerInternal(Batch<T> batch);
public abstract void SaveToCache(List<T> item);
public void DoneTraversing() => _checkCacheChannel.Writer.TryComplete();
public async Task DoneSaving()
{
if (!_checkCacheChannel.Reader.Completion.IsCompleted)
{
await _checkCacheChannel.Reader.Completion.ConfigureAwait(false);
}
}
public Exception? Exception { get; set; }
private void RecordException(Exception ex)
{
Exception = ex;
_checkCacheChannel.Writer.TryComplete(ex);
}
}
@@ -0,0 +1,37 @@
using System.Buffers;
using System.Threading.Channels;
using Open.ChannelExtensions;
namespace Speckle.Sdk.Serialisation.V2.Send;
public interface IHasByteSize
{
int ByteSize { get; }
}
public sealed class SizeBatchingChannelReader<T>(
ChannelReader<T> source,
int batchSize,
bool singleReader,
bool syncCont = false
)
: BatchingChannelReader<T, IMemoryOwner<T>>(
_ => BatchExtensions.CreateBatch<T>(),
source,
batchSize,
singleReader,
syncCont
)
where T : IHasByteSize
{
private readonly int _batchSize = batchSize;
protected override IMemoryOwner<T> CreateBatch(int capacity) => BatchExtensions.CreateBatch<T>();
protected override void TrimBatch(ref IMemoryOwner<T> batch, bool isVerifiedFull) =>
BatchExtensions.TrimBatch(ref batch, isVerifiedFull);
protected override void AddBatchItem(IMemoryOwner<T> batch, T item) => batch.AddBatchItem(item);
protected override int GetBatchSize(IMemoryOwner<T> batch) => batch.GetBatchSize(_batchSize);
}
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Compiler Properties">
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
<Configurations>Debug;Release;Local</Configurations>
<ILRepackTargetConfigurations>Debug;Release;Local</ILRepackTargetConfigurations>
<ILRepackRenameInternalized>true</ILRepackRenameInternalized>
<ILRepackMergeDebugSymbols>true</ILRepackMergeDebugSymbols>
</PropertyGroup>
<PropertyGroup Label="Nugetspec Package Properties">
<PackageId>Speckle.Sdk.Dependencies</PackageId>
<Description>The .NET SDK for Speckle</Description>
<PackageTags>$(PackageTags) core sdk</PackageTags>
</PropertyGroup>
<PropertyGroup Label="Nuget Package Properties">
<IsPackable>true</IsPackable>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="ILRepack.FullAuto">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.ObjectPool" PrivateAssets="all" />
<PackageReference Include="Polly" PrivateAssets="all" />
<PackageReference Include="Polly.Contrib.WaitAndRetry" PrivateAssets="all" />
<PackageReference Include="Polly.Extensions.Http" PrivateAssets="all" />
<PackageReference Include="Open.ChannelExtensions" PrivateAssets="all" />
<PackageReference Include="System.Threading.Channels" PrivateAssets="all" />
</ItemGroup>
</Project>
@@ -0,0 +1,80 @@
using Polly;
using Speckle.Sdk.Logging;
namespace Speckle.Sdk.Helpers;
internal sealed class SpeckleHttpClientHandler : DelegatingHandler
{
private readonly IAsyncPolicy<HttpResponseMessage> _resiliencePolicy;
private readonly ISdkActivityFactory _activityFactory;
internal SpeckleHttpClientHandler(
HttpMessageHandler innerHandler,
ISdkActivityFactory activityFactory,
IAsyncPolicy<HttpResponseMessage> resiliencePolicy
)
: base(innerHandler)
{
_activityFactory = activityFactory;
_resiliencePolicy = resiliencePolicy;
}
/// <exception cref="OperationCanceledException"><paramref name="cancellationToken"/> requested cancel</exception>
/// <exception cref="HttpRequestException">Send request failed</exception>
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken
)
{
// this is a preliminary client server correlation implementation
// refactor this, when we have a better observability stack
var context = new Context();
using var activity = _activityFactory.Start("Http Request");
{
activity?.SetTag("http.method", request.Method);
activity?.SetTag("http.url", request.RequestUri);
activity?.SetTag("correlationId", context.CorrelationId);
context.Add("retryCount", 0);
request.Headers.Add("x-request-id", context.CorrelationId.ToString());
activity?.InjectHeaders((k, v) => request.Headers.TryAddWithoutValidation(k, v));
var policyResult = await _resiliencePolicy
.ExecuteAndCaptureAsync(
ctx =>
{
return base.SendAsync(request, cancellationToken);
},
context
)
.ConfigureAwait(false);
context.TryGetValue("retryCount", out var retryCount);
activity?.SetTag("retryCount", retryCount);
if (policyResult.Outcome == OutcomeType.Successful)
{
activity?.SetStatus(SdkActivityStatusCode.Ok);
return policyResult.Result;
}
activity?.SetStatus(SdkActivityStatusCode.Error);
if (policyResult.FinalException is null)
{
// Outcome was not successful, but did not terminate with an exception (e.g. repeated 500 responses)
return policyResult.FinalHandledResult;
}
activity?.RecordException(policyResult.FinalException);
// if the policy failed due to a cancellation, AND it was our cancellation token, then don't wrap the exception, and rethrow an new cancellation
if (policyResult.FinalException is OperationCanceledException)
{
cancellationToken.ThrowIfCancellationRequested();
}
throw new HttpRequestException("Policy Failed", policyResult.FinalException);
}
}
}
@@ -0,0 +1,52 @@
using Polly;
using Polly.Contrib.WaitAndRetry;
using Polly.Extensions.Http;
using Polly.Timeout;
using Speckle.InterfaceGenerator;
using Speckle.Sdk.Logging;
namespace Speckle.Sdk.Helpers;
[GenerateAutoInterface]
public sealed class SpeckleHttpClientHandlerFactory(ISdkActivityFactory activityFactory)
: ISpeckleHttpClientHandlerFactory
{
public const int DEFAULT_TIMEOUT_SECONDS = 60;
public IEnumerable<TimeSpan> DefaultDelay()
{
return Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromMilliseconds(200), 5);
}
internal IAsyncPolicy<HttpResponseMessage> HttpAsyncPolicy(
IEnumerable<TimeSpan>? delay = null,
int timeoutSeconds = DEFAULT_TIMEOUT_SECONDS
)
{
var retryPolicy = HttpPolicyExtensions
.HandleTransientHttpError()
.Or<TimeoutRejectedException>()
.WaitAndRetryAsync(
delay ?? DefaultDelay(),
(ex, timeSpan, retryAttempt, context) =>
{
context.Remove("retryCount");
context.Add("retryCount", retryAttempt);
}
);
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(timeoutSeconds);
return Policy.WrapAsync(retryPolicy, timeoutPolicy);
}
public DelegatingHandler Create(
HttpMessageHandler? innerHandler = null,
int timeoutSeconds = DEFAULT_TIMEOUT_SECONDS
) =>
new SpeckleHttpClientHandler(
innerHandler ?? new HttpClientHandler(),
activityFactory,
HttpAsyncPolicy(timeoutSeconds: timeoutSeconds)
);
}
@@ -0,0 +1,253 @@
{
"version": 2,
"dependencies": {
".NETStandard,Version=v2.0": {
"ILRepack.FullAuto": {
"type": "Direct",
"requested": "[1.6.0, )",
"resolved": "1.6.0",
"contentHash": "34qp/HQ0XRIWCjtNGUOslJ6p9eNWqHXZQ+xx1iBCvXy3mj8tEiqIwRG+LubFyKCJITqMh5cpFvFl20/6+Dmy+g==",
"dependencies": {
"ILRepack": "2.0.33"
}
},
"Microsoft.Extensions.ObjectPool": {
"type": "Direct",
"requested": "[9.0.4, )",
"resolved": "9.0.4",
"contentHash": "G7p1k2xVZ+2aVANz0JdSiafr+AHDHeS1kF8+Y0ABbIsByd0erOL59IDXBs9vcdJf3pPV/murO0mbtr4k40QxWw=="
},
"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"
}
},
"Open.ChannelExtensions": {
"type": "Direct",
"requested": "[9.1.0, )",
"resolved": "9.1.0",
"contentHash": "D6c24vMGy1oZ06vmkD2/FNzWHK7ZIihuv2spDgYEeaUp+eobrILQnrNQKRoASFXD4JGfZ7nfvTM0e+AX79dt8Q==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4",
"System.Collections.Immutable": "9.0.4",
"System.Threading.Channels": "9.0.4"
}
},
"Polly": {
"type": "Direct",
"requested": "[7.2.3, )",
"resolved": "7.2.3",
"contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ=="
},
"Polly.Contrib.WaitAndRetry": {
"type": "Direct",
"requested": "[1.1.1, )",
"resolved": "1.1.1",
"contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA=="
},
"Polly.Extensions.Http": {
"type": "Direct",
"requested": "[3.0.0, )",
"resolved": "3.0.0",
"contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==",
"dependencies": {
"Polly": "7.1.0"
}
},
"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.Threading.Channels": {
"type": "Direct",
"requested": "[9.0.4, )",
"resolved": "9.0.4",
"contentHash": "4qBn2H6/aXBpE/Pm3wY5yusY/pEvQz99NlWHrTUji0qCmOdbhhjaALcpmbfW2ksxlPM6i6S+QFLkpOQdyfeKYQ==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4",
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"ILRepack": {
"type": "Transitive",
"resolved": "2.0.33",
"contentHash": "xb2h1CsOepoYwdXEPui9VcQglwABQwNf9cccZbf+acarEzF5PUp8Xx71nFXIhOgEdm6wrxAoF6xAxK4m/XFRUQ=="
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.NETCore.Platforms": {
"type": "Transitive",
"resolved": "1.1.0",
"contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"System.Buffers": {
"type": "Transitive",
"resolved": "4.5.1",
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
},
"System.Collections.Immutable": {
"type": "Transitive",
"resolved": "9.0.4",
"contentHash": "wfm2NgK22MmBe5qJjp52qzpkeDZKb4l9LbdubhZSehY1z4LS+lld6R+B+UQNb2AZRHu/QJlHxEUcRst5hIEejg==",
"dependencies": {
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"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.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
},
"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"
}
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "CentralTransitive",
"requested": "[5.0.0, )",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
}
},
"net8.0": {
"ILRepack.FullAuto": {
"type": "Direct",
"requested": "[1.6.0, )",
"resolved": "1.6.0",
"contentHash": "34qp/HQ0XRIWCjtNGUOslJ6p9eNWqHXZQ+xx1iBCvXy3mj8tEiqIwRG+LubFyKCJITqMh5cpFvFl20/6+Dmy+g==",
"dependencies": {
"ILRepack": "2.0.33"
}
},
"Microsoft.Extensions.ObjectPool": {
"type": "Direct",
"requested": "[9.0.4, )",
"resolved": "9.0.4",
"contentHash": "G7p1k2xVZ+2aVANz0JdSiafr+AHDHeS1kF8+Y0ABbIsByd0erOL59IDXBs9vcdJf3pPV/murO0mbtr4k40QxWw=="
},
"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"
}
},
"Open.ChannelExtensions": {
"type": "Direct",
"requested": "[9.1.0, )",
"resolved": "9.1.0",
"contentHash": "D6c24vMGy1oZ06vmkD2/FNzWHK7ZIihuv2spDgYEeaUp+eobrILQnrNQKRoASFXD4JGfZ7nfvTM0e+AX79dt8Q=="
},
"Polly": {
"type": "Direct",
"requested": "[7.2.3, )",
"resolved": "7.2.3",
"contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ=="
},
"Polly.Contrib.WaitAndRetry": {
"type": "Direct",
"requested": "[1.1.1, )",
"resolved": "1.1.1",
"contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA=="
},
"Polly.Extensions.Http": {
"type": "Direct",
"requested": "[3.0.0, )",
"resolved": "3.0.0",
"contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==",
"dependencies": {
"Polly": "7.1.0"
}
},
"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.Threading.Channels": {
"type": "Direct",
"requested": "[9.0.4, )",
"resolved": "9.0.4",
"contentHash": "4qBn2H6/aXBpE/Pm3wY5yusY/pEvQz99NlWHrTUji0qCmOdbhhjaALcpmbfW2ksxlPM6i6S+QFLkpOQdyfeKYQ=="
},
"ILRepack": {
"type": "Transitive",
"resolved": "2.0.33",
"contentHash": "xb2h1CsOepoYwdXEPui9VcQglwABQwNf9cccZbf+acarEzF5PUp8Xx71nFXIhOgEdm6wrxAoF6xAxK4m/XFRUQ=="
},
"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=="
}
}
}
}
+111
View File
@@ -0,0 +1,111 @@
using Speckle.Sdk.Api.GraphQL;
using Speckle.Sdk.Api.GraphQL.Models;
namespace Speckle.Sdk.Api;
/// <summary>
/// The base class for all GraphQL errors (these are errors in the graphql response)
/// Some specific codes are maped to subtypes <see cref="GraphQLErrorHandler"/>
/// <seealso cref="SpeckleGraphQLForbiddenException"/>
/// <seealso cref="SpeckleGraphQLInternalErrorException"/>
/// <seealso cref="SpeckleGraphQLBadInputException"/>
/// <seealso cref="SpeckleGraphQLInvalidQueryException"/>
/// </summary>
public class SpeckleGraphQLException : SpeckleException
{
public SpeckleGraphQLException() { }
public SpeckleGraphQLException(string? message)
: base(message) { }
public SpeckleGraphQLException(string? message, Exception? innerException)
: base(message, innerException) { }
}
/// <summary>
/// Represents a "FORBIDDEN" or "UNAUTHORIZED" GraphQL error as an exception.
/// https://www.apollographql.com/docs/apollo-server/v2/data/errors/#unauthenticated
/// https://www.apollographql.com/docs/apollo-server/v2/data/errors/#forbidden
/// </summary>
public sealed class SpeckleGraphQLForbiddenException : SpeckleGraphQLException
{
public SpeckleGraphQLForbiddenException() { }
public SpeckleGraphQLForbiddenException(string? message)
: base(message) { }
public SpeckleGraphQLForbiddenException(string? message, Exception? innerException)
: base(message, innerException) { }
}
/// <summary>
/// Represents a "INTERNAL_SERVER_ERROR" GraphQL error as an exception.
/// https://www.apollographql.com/docs/apollo-server/v2/data/errors#internal_server_error
/// </summary>
public sealed class SpeckleGraphQLInternalErrorException : SpeckleGraphQLException
{
public SpeckleGraphQLInternalErrorException() { }
public SpeckleGraphQLInternalErrorException(string? message)
: base(message) { }
public SpeckleGraphQLInternalErrorException(string? message, Exception? innerException)
: base(message, innerException) { }
}
/// <summary>
/// Represents the custom "STREAM_NOT_FOUND" GraphQL error as an exception.
/// </summary>
public sealed class SpeckleGraphQLStreamNotFoundException : SpeckleGraphQLException
{
public SpeckleGraphQLStreamNotFoundException() { }
public SpeckleGraphQLStreamNotFoundException(string? message)
: base(message) { }
public SpeckleGraphQLStreamNotFoundException(string? message, Exception? innerException)
: base(message, innerException) { }
}
/// <summary>
/// Represents a "BAD_USER_INPUT" GraphQL error as an exception.
/// https://www.apollographql.com/docs/apollo-server/v2/data/errors#bad_user_input
/// </summary>
public sealed class SpeckleGraphQLBadInputException : SpeckleGraphQLException
{
public SpeckleGraphQLBadInputException() { }
public SpeckleGraphQLBadInputException(string? message)
: base(message) { }
public SpeckleGraphQLBadInputException(string? message, Exception? innerException)
: base(message, innerException) { }
}
/// <summary>
/// Represents a "GRAPHQL_PARSE_FAILED" or "GRAPHQL_VALIDATION_FAILED" GraphQL error as an exception.
/// https://www.apollographql.com/docs/apollo-server/v2/data/errors#graphql_parse_failed
/// https://www.apollographql.com/docs/apollo-server/v2/data/errors#graphql_validation_failed
/// </summary>
public sealed class SpeckleGraphQLInvalidQueryException : SpeckleGraphQLException
{
public SpeckleGraphQLInvalidQueryException() { }
public SpeckleGraphQLInvalidQueryException(string? message)
: base(message) { }
public SpeckleGraphQLInvalidQueryException(string? message, Exception? innerException)
: base(message, innerException) { }
}
/// <seealso cref="PermissionCheckResult"/>
public sealed class WorkspacePermissionException : SpeckleGraphQLException
{
public WorkspacePermissionException() { }
public WorkspacePermissionException(string? message)
: base(message) { }
public WorkspacePermissionException(string? message, Exception? innerException)
: base(message, innerException) { }
}
+10
View File
@@ -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 = pascal_case
+249
View File
@@ -0,0 +1,249 @@
using System.Diagnostics.CodeAnalysis;
using System.Net.WebSockets;
using System.Reflection;
using GraphQL;
using GraphQL.Client.Http;
using Microsoft.Extensions.Logging;
using Speckle.InterfaceGenerator;
using Speckle.Newtonsoft.Json;
using Speckle.Newtonsoft.Json.Serialization;
using Speckle.Sdk.Api.GraphQL;
using Speckle.Sdk.Api.GraphQL.Resources;
using Speckle.Sdk.Api.GraphQL.Serializer;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Dependencies;
using Speckle.Sdk.Helpers;
using Speckle.Sdk.Logging;
namespace Speckle.Sdk.Api;
public partial interface IClient : IDisposable
{
GraphQLHttpClient GQLClient { get; }
}
[SuppressMessage("Maintainability", "CA1506:Avoid excessive class coupling", Justification = "Class needs refactor")]
[GenerateAutoInterface]
public sealed class Client : ISpeckleGraphQLClient, IClient
{
private readonly ILogger<Client> _logger;
private readonly ISdkActivityFactory _activityFactory;
public ProjectResource Project { get; }
public ModelResource Model { get; }
public VersionResource Version { get; }
public ActiveUserResource ActiveUser { get; }
public OtherUserResource OtherUser { get; }
public ProjectInviteResource ProjectInvite { get; }
public CommentResource Comment { get; }
public SubscriptionResource Subscription { get; }
public WorkspaceResource Workspace { get; }
public Uri ServerUrl => new(Account.serverInfo.url);
[JsonIgnore]
public Account Account { get; }
private HttpClient HttpClient { get; }
[AutoInterfaceIgnore]
public GraphQLHttpClient GQLClient { get; }
/// <param name="account"></param>
/// <exception cref="ArgumentException"><paramref name="account"/> was null</exception>
public Client(
ILogger<Client> logger,
ISdkActivityFactory activityFactory,
ISpeckleApplication application,
ISpeckleHttp speckleHttp,
Account account
)
{
_logger = logger;
_activityFactory = activityFactory;
Account = account ?? throw new ArgumentException("Provided account is null.");
Project = new(this);
Model = new(this);
Version = new(this);
ActiveUser = new(this);
OtherUser = new(this);
ProjectInvite = new(this);
Comment = new(this);
Subscription = new(this);
Workspace = new(this);
HttpClient = CreateHttpClient(application, speckleHttp, account);
GQLClient = CreateGraphQLClient(account, HttpClient);
}
[AutoInterfaceIgnore]
public void Dispose()
{
try
{
Subscription.Dispose();
GQLClient.Dispose();
}
catch (Exception ex) when (!ex.IsFatal()) { }
}
internal async Task<T> ExecuteWithResiliencePolicies<T>(Func<Task<T>> func) =>
await GraphQLRetry
.ExecuteAsync<T, SpeckleGraphQLInternalErrorException>(
func,
(
(ex, timeout) =>
{
_logger.LogDebug(
ex,
"The previous attempt at executing function to get {resultType} failed with {exceptionMessage}. Retrying after {timeout}",
typeof(T).Name,
ex.Message,
timeout
);
}
)
)
.ConfigureAwait(false);
/// <inheritdoc/>
public async Task<T> ExecuteGraphQLRequest<T>(GraphQLRequest request, CancellationToken cancellationToken = default)
{
using var activity = _activityFactory.Start();
activity?.SetTag("responseType", typeof(T));
activity?.SetTag("request.query", request.Query);
activity?.SetTag("request.operationName", request.OperationName);
activity?.SetTag("request.variables", request.Variables);
activity?.SetTag("request.extensions", request.Extensions);
activity?.SetTag("clientOptions.endPoint", GQLClient.Options.EndPoint);
activity?.SetTag("clientOptions.medaType", GQLClient.Options.MediaType);
activity?.SetTag("clientOptions.webSocketEndPoint", GQLClient.Options.WebSocketEndPoint);
activity?.SetTag("clientOptions.webSocketProtocol", GQLClient.Options.WebSocketProtocol);
try
{
var ret = await ExecuteWithResiliencePolicies(async () =>
{
GraphQLResponse<T> result = await GQLClient
.SendMutationAsync<T>(request, cancellationToken)
.ConfigureAwait(false);
result.EnsureGraphQLSuccess();
return result.Data;
})
.ConfigureAwait(false);
activity?.SetStatus(SdkActivityStatusCode.Ok);
return ret;
}
catch (Exception ex)
{
activity?.SetStatus(SdkActivityStatusCode.Error);
activity?.RecordException(ex);
throw;
}
}
[AutoInterfaceIgnore]
IDisposable ISpeckleGraphQLClient.SubscribeTo<T>(GraphQLRequest request, Action<object, T> callback) =>
SubscribeTo(request, callback);
/// <inheritdoc cref="ISpeckleGraphQLClient.SubscribeTo{T}"/>
private IDisposable SubscribeTo<T>(GraphQLRequest request, Action<object, T> callback)
{
try
{
var res = GQLClient.CreateSubscriptionStream<T>(request);
return res.Subscribe(
response =>
{
try
{
response.EnsureGraphQLSuccess();
callback(this, response.Data);
}
catch (AggregateException ex)
{
_logger.LogWarning(ex, "Subscription for {type} got a response with errors", typeof(T).Name);
throw;
}
},
ex =>
{
// we're logging this as an error for now, to keep track of failures
// so far we've swallowed these errors
_logger.LogError(
ex,
"Subscription for {resultType} terminated unexpectedly with {exceptionMessage}",
typeof(T).Name,
ex.Message
);
// we could be throwing like this:
// throw ex;
}
);
}
catch (Exception ex) when (!ex.IsFatal() && ex is not ObjectDisposedException)
{
throw new SpeckleGraphQLException($"Subscription for {typeof(T)} failed to start", ex);
}
}
private static GraphQLHttpClient CreateGraphQLClient(Account account, HttpClient httpClient)
{
var gQLClient = new GraphQLHttpClient(
new GraphQLHttpClientOptions
{
EndPoint = new Uri(new Uri(account.serverInfo.url), "/graphql"),
UseWebSocketForQueriesAndMutations = false,
WebSocketProtocol = "graphql-ws",
ConfigureWebSocketConnectionInitPayload = _ =>
{
return SpeckleHttp.CanAddAuth(account.token, out string? authValue)
? new { Authorization = authValue }
: null;
},
},
new NewtonsoftJsonSerializer(
new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver { IgnoreIsSpecifiedMembers = true }, //(Default)
MissingMemberHandling = MissingMemberHandling.Error, //(not default) If you query for a member that doesn't exist, this will throw (except websocket responses see https://github.com/graphql-dotnet/graphql-client/issues/660)
Converters =
{
new ConstantCaseEnumConverter(),
} //(Default) enums will be serialized using the GraphQL const case standard
,
}
),
httpClient
);
gQLClient.WebSocketReceiveErrors.Subscribe(e =>
{
if (e is WebSocketException we)
{
Console.WriteLine(
$"WebSocketException: {we.Message} (WebSocketError {we.WebSocketErrorCode}, ErrorCode {we.ErrorCode}, NativeErrorCode {we.NativeErrorCode}"
);
}
else
{
Console.WriteLine($"Exception in websocket receive stream: {e}");
}
});
return gQLClient;
}
private static HttpClient CreateHttpClient(ISpeckleApplication application, ISpeckleHttp speckleHttp, Account account)
{
var httpClient = speckleHttp.CreateHttpClient(timeoutSeconds: 30, authorizationToken: account.token);
httpClient.DefaultRequestHeaders.Add("apollographql-client-name", application.ApplicationAndVersion);
httpClient.DefaultRequestHeaders.Add(
"apollographql-client-version",
Assembly.GetExecutingAssembly().GetName().Version?.ToString()
);
return httpClient;
}
}
@@ -0,0 +1,19 @@
using Microsoft.Extensions.Logging;
using Speckle.InterfaceGenerator;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Helpers;
using Speckle.Sdk.Logging;
namespace Speckle.Sdk.Api;
[GenerateAutoInterface]
public class ClientFactory(
ILoggerFactory loggerFactory,
ISdkActivityFactory activityFactory,
ISpeckleApplication application,
ISpeckleHttp speckleHttp
) : IClientFactory
{
public IClient Create(Account account) =>
new Client(loggerFactory.CreateLogger<Client>(), activityFactory, application, speckleHttp, account);
}
@@ -0,0 +1,10 @@
namespace Speckle.Sdk.Api.GraphQL.Enums;
//This enum isn't explicitly defined in the schema, instead its usages are int typed (But represent an enum)
public enum FileUploadConversionStatus
{
Queued = 0,
Processing = 1,
Success = 2,
Error = 3,
}
@@ -0,0 +1,8 @@
namespace Speckle.Sdk.Api.GraphQL.Enums;
public enum ProjectCommentsUpdatedMessageType
{
ARCHIVED,
CREATED,
UPDATED,
}
@@ -0,0 +1,7 @@
namespace Speckle.Sdk.Api.GraphQL.Enums;
public enum ProjectFileImportUpdatedMessageType
{
CREATED,
UPDATED,
}

Some files were not shown because too many files have changed in this diff Show More