Compare commits

...

72 Commits

Author SHA1 Message Date
Ralph Wessel 8e80d5ddd5 Merge branch 'develop' into david/cnx-150-implement-selectionbinding 2024-10-16 15:04:07 +01:00
David Kekesi 3813c8740f SelectionInfo::initialize changed to private 2024-10-16 15:53:59 +02:00
David Kekesi 5c1511e850 code cleanup 2024-10-16 15:50:15 +02:00
David Kekesi 511311eb84 selectionBridge works 2024-10-16 15:08:34 +02:00
Ralph Wessel ebed71a791 Removed unused static var from SelectionSubscriber 2024-10-16 11:03:38 +01:00
Ralph Wessel 3e7b78e50d Allow SelectionSubscriber to start/stop outside initialisation phase 2024-10-16 10:31:11 +01:00
Ralph Wessel 2d23eb6b28 Fixes to Column and ColumnSegment classes 2024-10-15 23:05:07 +01:00
Ralph Wessel ffb9cf0cef Updated VS projects 2024-10-15 21:12:37 +01:00
Ralph Wessel e54962e76f Added Column support:
- Column element
- ColumnSegment element
- SegmentedColumn for managing segment retrieval
- Segment/Path interfaces
- Element Part interface
GenericElement now represents all element types with no specific class
Element becomes abstract base for all elements
Added Memo to handle supplementary element data
Added BIMMemory for common BIM memory utilities
BIMElementDatabase adds Memo table support
ArchicadElementDBaseEngine creates GenericElement, Column or ColumnSegment elements
2024-10-15 21:05:57 +01:00
David Kekesi def9162e4c added SelectionBridge, SelectionInfo, GetSelection 2024-10-15 17:13:06 +02:00
Ralph Wessel 9c10300f01 Merge pull request #6 from specklesystems/FinishProxies_WIP
Finish proxies wip
2024-10-14 16:04:52 +01:00
David Kekesi 902fb0f626 changed ArchicadRGB::write to export ARGB colors instead of RGB 2024-10-14 15:46:12 +02:00
Ralph Wessel 432e5a0b0b Merge branch 'FinishProxies_WIP' of https://github.com/specklesystems/speckle-cpp-connectors into FinishProxies_WIP 2024-10-14 11:25:32 +01:00
Ralph Wessel a67361c903 FinishProxy should call base object when (de)serialising 2024-10-14 11:25:09 +01:00
Ralph Wessel e82698ac3a Shininess in Modeler::Material is 1-100 scale (NB: looks logarithmic but will resolve on advice from GS) 2024-10-14 11:18:48 +01:00
Ralph Wessel 2fab7da57d Update FinishProxy to be a BIMRecord, i.e. export speckle_type and applicaiton ID
BIMRecord constructor should allow units to be initialised to nullopt
2024-10-14 11:17:17 +01:00
Ralph Wessel 36b95436ae Finishes have no unit of measurement 2024-10-14 10:13:23 +01:00
Ralph Wessel 24f6a6349a Capture ID of Modeler::Material in Finish 2024-10-14 10:00:23 +01:00
Ralph Wessel bd54ae3995 Change JSON tag from "unit" to "units" 2024-10-14 09:50:07 +01:00
Ralph Wessel 7e742796f5 Corrected Mesh speckle_type
Mesh constructor failed to set BIM ID
2024-10-13 22:27:59 +01:00
Ralph Wessel 373179fa65 Assigned serialisation type for Str256
Linked mesh IDs in finish proxies is a single array
2024-10-13 22:16:52 +01:00
Ralph Wessel 11bfb9333f Updated VS projects 2024-10-13 21:35:10 +01:00
Ralph Wessel 45c0705a60 #include <ACAPinc.h> prior to other includes 2024-10-13 19:57:01 +01:00
Ralph Wessel 7f4b7112ae Finishes incorporates all Archicad material properties
Serialisation wrapper for Archicad colour rgb colour
2024-10-13 17:49:17 +01:00
Ralph Wessel 05c2cdc47a Updated VS projects
Using Str256 for AC attribute name
WIP - using fixed size for material proxy serialisation count (actual count not known at this point - may need to use something like max size_t)
2024-10-12 09:19:09 +01:00
Ralph Wessel dbc2c79f2a Added serilaisation wrapper for 256-byte char array (used for some AC string types) 2024-10-12 09:06:32 +01:00
Ralph Wessel 36e50bb63e SendObject and SendViaBrowserArgs forward the wrapped object management to the serialiser
ProjectCollection defines itself as a serialisation manager (to collect material proxies)
2024-10-11 17:44:41 +01:00
Ralph Wessel d3492512cd Update SendObject and SendViaBrowserArgs to use serialisation management (to support collection of material proxies) 2024-10-11 17:19:04 +01:00
Ralph Wessel 65b636accb Mesh serialisation now directly engages with management to populate material proxies rather than using a "dummy" field 2024-10-11 16:53:42 +01:00
David Kekesi 791e9f072f finish proxies wip 2024-10-11 17:10:05 +02:00
kekesidavid 68c98abde9 Merge pull request #5 from specklesystems/david/cnx-576-implement-element-getbody
David/cnx 576 implement element getbody
2024-10-11 13:20:44 +02:00
Ralph Wessel a117e327c7 Updated VS project 2024-10-11 11:47:35 +01:00
Ralph Wessel de46d899b0 Added FinishProxy to collect mesh finishes (to build 'material proxy collection) 2024-10-11 11:45:10 +01:00
David Kekesi 30163bfd9d export mesh, vertices, faces, colors 2024-10-11 12:44:55 +02:00
Ralph Wessel 21fc2cff25 Added FinishCollector interface as a serialisation manager to collect finishes from meshes as they are serialised
ProjectCollection is now a FinishCollector (accumulates the finishes to write the material proxies at the end)
2024-10-11 09:58:26 +01:00
Ralph Wessel 0c74a28982 Fixes to Storey attribute class 2024-10-10 13:45:36 +01:00
Ralph Wessel cc64d7df44 Added lookup for AC storeys using an element floor index 2024-10-10 13:02:57 +01:00
Ralph Wessel f7112a407c Set correct base class for RecordCollection
RecordCollection serialises name
2024-10-10 12:12:35 +01:00
Ralph Wessel 7ed4d8c807 Updated VS projects 2024-10-10 10:45:35 +01:00
Ralph Wessel b5447b263e NB: Interim commit - new code is untested at this point
Information sent to Speckle is now presented in hiearchical collections:
- Root level contains material proxies
- Second level is project storeys
- Third level is element type
Added database and record definitions for attributes including:
- Finishes (surface rendering material)
- Storeys
Element getters for storey and type name
Aligned some speckle_type names to Revit (where possible)
2024-10-10 08:52:18 +01:00
Ralph Wessel b51b0ecb13 Mesh serialises with speckle_type and units - now appears in 3D view 2024-10-04 22:13:32 +01:00
Ralph Wessel 72cd75d2e6 Added speckle_type and unit properties to Record, BIMRecord, Element and Mesh classes
Defined (de)serialisation for LengthType
2024-10-04 21:03:54 +01:00
Ralph Wessel 3e2777a38f Merge branch 'david/cnx-577-implement-mesh-class' into develop 2024-10-03 17:55:44 +01:00
Ralph Wessel 042a515dc4 Removed test code from palette 2024-10-03 17:54:01 +01:00
Ralph Wessel f5bb3dc454 Fix to element mesh face indices 2024-10-03 17:50:50 +01:00
Ralph Wessel f066b0ee02 Send bridge method injects the first selected element into the sent data for testing
Mesh vertices, faces and colours are all single arrays
2024-10-03 17:05:02 +01:00
Ralph Wessel 8dd00005b1 Element is a type of BIMRecord 2024-10-03 16:39:21 +01:00
Ralph Wessel 0592bcc947 BIMRecord is a type of Record (adds applicationID) 2024-10-03 16:31:58 +01:00
Ralph Wessel 76348c8fd1 Merge branch 'develop' into david/cnx-577-implement-mesh-class 2024-10-03 15:51:27 +01:00
Ralph Wessel 1a61608c42 Using preview interface for testing 2024-10-03 14:33:52 +01:00
Ralph Wessel 844ab3544b Commit working 2024-10-03 14:32:30 +01:00
Ralph Wessel 990a1ab9e1 Updated for VS 2024-10-03 13:48:22 +01:00
David Kekesi 6cf03010bd mesh export WIP 2024-10-03 14:39:09 +02:00
Ralph Wessel a0413d0d3a SendObject now holds and sends a Record 2024-10-03 13:12:57 +01:00
Ralph Wessel 8539129e7c SendObject now the owner of the object to be sent 2024-10-03 12:33:08 +01:00
Ralph Wessel 1a901b0a6c Send bridge is updated:
- total children removed
- rootObject replaces batches (sent as an object rather than a string)
2024-10-03 12:05:48 +01:00
Ralph Wessel cc034191b4 Updated with GetSendSettings 2024-10-02 12:54:48 +01:00
Ralph Wessel a6911fad1c Added GetSendSettings binding 2024-10-02 10:09:28 +01:00
Ralph Wessel fdf371732b Updated serialisation for send objects 2024-10-02 09:30:53 +01:00
Ralph Wessel e9f8e86f95 Dummy information in SendObject 2024-10-01 18:58:50 +01:00
Ralph Wessel 8eae1e9967 Updated VS projects 2024-10-01 18:24:33 +01:00
Ralph Wessel 8ed2abea04 Implemented skeleton of SendObject 2024-10-01 18:21:30 +01:00
Ralph Wessel e148094c81 ModelCardDatabase can retrieve a card by ID
Added numerous Send argument classes:
- ConversionResult (incomplete)
- SendError
- SendObject
- SendViaBrowserArgs (incomplete)
Send method looks up model card and account details, constructs send arguments
AccountDatabase can find an account by ID or server URL
Added DetachedMemoryStore
2024-10-01 17:24:59 +01:00
Ralph Wessel deee1e80c5 Added classes for managing detached objects:
- DetachmentManager: Manages detached objects during (de)serialisation
- DetachedWrap: Wrapper for detached objects, generating references on demand from serialised data
- DetachedReference: Wrapper for representation of wrapped objects with a reference
- DetachedObjectStore: Interface for filing/retrieving detached objects in storage
2024-09-30 10:50:24 +01:00
Ralph Wessel 4c8a2237bf Removed testing code 2024-09-26 13:28:14 +01:00
Ralph Wessel 5abc831473 Implemented Archicad element database functionality:
- Retrieving the database from the Project
- Gettting selected elements
- Getting an element from an index/link
2024-09-26 13:05:22 +01:00
Ralph Wessel b8d952c9a3 Updated Send method to trigger a browser event rather than throwing an exception
Added new resources
2024-09-25 13:29:33 +01:00
Ralph Wessel 7baedf707a Updated VS projects 2024-09-24 22:29:40 +01:00
Ralph Wessel 4dd0a1b1b6 Added skeleton BIM database, element, collection and Mesh classes to support sending model data 2024-09-24 21:50:28 +01:00
Ralph Wessel 29ece2282f Added DocumentStoreEngine::getUniqueID
Added ModelCardDatabase::getStoreID
2024-09-23 16:01:35 +01:00
Ralph Wessel f0fc7e8fec Merge tag '0.2.4' into develop
0.2.4
2024-09-23 14:10:34 +01:00
Ralph Wessel 8dceee52f2 Merge branch 'release/0.2.4' 2024-09-23 14:10:34 +01:00
120 changed files with 8888 additions and 130 deletions
+22
View File
@@ -111,7 +111,15 @@
<ClCompile Include="Connector\Interface\Browser\Bridge\Config\GetConfig.cpp" />
<ClCompile Include="Connector\Interface\Browser\Bridge\Config\GetIsDevMode.cpp" />
<ClCompile Include="Connector\Interface\Browser\Bridge\Config\UpdateConfig.cpp" />
<ClCompile Include="Connector\Interface\Browser\Bridge\Selection\Arg\SelectionInfo.cpp" />
<ClCompile Include="Connector\Interface\Browser\Bridge\Selection\GetSelection.cpp" />
<ClCompile Include="Connector\Interface\Browser\Bridge\Selection\SelectionBridge.cpp" />
<ClCompile Include="Connector\Interface\Browser\Bridge\Send\Arg\ConversionResult.cpp" />
<ClCompile Include="Connector\Interface\Browser\Bridge\Send\Arg\SendError.cpp" />
<ClCompile Include="Connector\Interface\Browser\Bridge\Send\Arg\SendObject.cpp" />
<ClCompile Include="Connector\Interface\Browser\Bridge\Send\Arg\SendViaBrowserArgs.cpp" />
<ClCompile Include="Connector\Interface\Browser\Bridge\Send\GetSendFilters.cpp" />
<ClCompile Include="Connector\Interface\Browser\Bridge\Send\GetSendSettings.cpp" />
<ClCompile Include="Connector\Interface\Browser\Bridge\Send\Send.cpp" />
<ClCompile Include="Connector\Interface\Browser\Bridge\Send\SendBridge.cpp" />
<ClCompile Include="Connector\Interface\Browser\Bridge\Test\Arg\SayHiArg.cpp" />
@@ -122,6 +130,9 @@
<ClCompile Include="Connector\Interface\Browser\Bridge\Test\TriggerEvent.cpp" />
<ClCompile Include="Connector\Interface\ConnectorMenu.cpp" />
<ClCompile Include="Connector\Interface\ConnectorPalette.cpp" />
<ClCompile Include="Connector\Record\Collection\FinishProxy.cpp" />
<ClCompile Include="Connector\Record\Collection\ProjectCollection.cpp" />
<ClCompile Include="Connector\Record\Collection\RecordCollection.cpp" />
<ClCompile Include="Connector\Record\Model\CardMover.cpp" />
<ClCompile Include="Connector\Record\Model\CardSetting.cpp" />
<ClCompile Include="Connector\Record\Model\Filter\DirectSelectionSendFilter.cpp" />
@@ -155,7 +166,15 @@
<ClInclude Include="Connector\Interface\Browser\Bridge\Config\GetConfig.h" />
<ClInclude Include="Connector\Interface\Browser\Bridge\Config\GetIsDevMode.h" />
<ClInclude Include="Connector\Interface\Browser\Bridge\Config\UpdateConfig.h" />
<ClInclude Include="Connector\Interface\Browser\Bridge\Selection\Arg\SelectionInfo.h" />
<ClInclude Include="Connector\Interface\Browser\Bridge\Selection\GetSelection.h" />
<ClInclude Include="Connector\Interface\Browser\Bridge\Selection\SelectionBridge.h" />
<ClInclude Include="Connector\Interface\Browser\Bridge\Send\Arg\ConversionResult.h" />
<ClInclude Include="Connector\Interface\Browser\Bridge\Send\Arg\SendError.h" />
<ClInclude Include="Connector\Interface\Browser\Bridge\Send\Arg\SendObject.h" />
<ClInclude Include="Connector\Interface\Browser\Bridge\Send\Arg\SendViaBrowserArgs.h" />
<ClInclude Include="Connector\Interface\Browser\Bridge\Send\GetSendFilters.h" />
<ClInclude Include="Connector\Interface\Browser\Bridge\Send\GetSendSettings.h" />
<ClInclude Include="Connector\Interface\Browser\Bridge\Send\Send.h" />
<ClInclude Include="Connector\Interface\Browser\Bridge\Send\SendBridge.h" />
<ClInclude Include="Connector\Interface\Browser\Bridge\Test\Arg\SayHiArg.h" />
@@ -166,6 +185,9 @@
<ClInclude Include="Connector\Interface\Browser\Bridge\Test\TriggerEvent.h" />
<ClInclude Include="Connector\Interface\ConnectorMenu.h" />
<ClInclude Include="Connector\Interface\ConnectorPalette.h" />
<ClInclude Include="Connector\Record\Collection\FinishProxy.h" />
<ClInclude Include="Connector\Record\Collection\ProjectCollection.h" />
<ClInclude Include="Connector\Record\Collection\RecordCollection.h" />
<ClInclude Include="Connector\Record\Model\CardMover.h" />
<ClInclude Include="Connector\Record\Model\CardSetting.h" />
<ClInclude Include="Connector\Record\Model\Filter\ArchicadEverythingFilter.h" />
@@ -59,6 +59,18 @@
<Filter Include="Connector\Record\Model\Filter">
<UniqueIdentifier>{b6d6326c-77f4-414a-bda6-e3e587c7ded2}</UniqueIdentifier>
</Filter>
<Filter Include="Connector\Record\Collection">
<UniqueIdentifier>{0ac4b0a4-6a2a-4a48-9757-1172effc20e7}</UniqueIdentifier>
</Filter>
<Filter Include="Connector\Interface\Browser\Bridge\Send\Arg">
<UniqueIdentifier>{6693f9a9-5ece-4853-b008-4064d1c551ab}</UniqueIdentifier>
</Filter>
<Filter Include="Connector\Interface\Browser\Bridge\Selection">
<UniqueIdentifier>{806f4af5-fa02-49b8-ac01-297991fe90ea}</UniqueIdentifier>
</Filter>
<Filter Include="Connector\Interface\Browser\Bridge\Selection\Arg">
<UniqueIdentifier>{8bb3df60-affe-4b66-8d78-f1b98e6ba8df}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="RFIX.win\Connector.rc2">
@@ -198,6 +210,39 @@
<ClCompile Include="Connector\Interface\Browser\Bridge\Base\UpdateModel.cpp">
<Filter>Connector\Interface\Browser\Bridge\Base</Filter>
</ClCompile>
<ClCompile Include="Connector\Record\Collection\RecordCollection.cpp">
<Filter>Connector\Record\Collection</Filter>
</ClCompile>
<ClCompile Include="Connector\Interface\Browser\Bridge\Send\Arg\ConversionResult.cpp">
<Filter>Connector\Interface\Browser\Bridge\Send\Arg</Filter>
</ClCompile>
<ClCompile Include="Connector\Interface\Browser\Bridge\Send\Arg\SendError.cpp">
<Filter>Connector\Interface\Browser\Bridge\Send\Arg</Filter>
</ClCompile>
<ClCompile Include="Connector\Interface\Browser\Bridge\Send\Arg\SendViaBrowserArgs.cpp">
<Filter>Connector\Interface\Browser\Bridge\Send\Arg</Filter>
</ClCompile>
<ClCompile Include="Connector\Interface\Browser\Bridge\Send\Arg\SendObject.cpp">
<Filter>Connector\Interface\Browser\Bridge\Send\Arg</Filter>
</ClCompile>
<ClCompile Include="Connector\Interface\Browser\Bridge\Send\GetSendSettings.cpp">
<Filter>Connector\Interface\Browser\Bridge\Send</Filter>
</ClCompile>
<ClCompile Include="Connector\Record\Collection\FinishProxy.cpp">
<Filter>Connector\Record\Collection</Filter>
</ClCompile>
<ClCompile Include="Connector\Record\Collection\ProjectCollection.cpp">
<Filter>Connector\Record\Collection</Filter>
</ClCompile>
<ClCompile Include="Connector\Interface\Browser\Bridge\Selection\GetSelection.cpp">
<Filter>Connector\Interface\Browser\Bridge\Selection</Filter>
</ClCompile>
<ClCompile Include="Connector\Interface\Browser\Bridge\Selection\SelectionBridge.cpp">
<Filter>Connector\Interface\Browser\Bridge\Selection</Filter>
</ClCompile>
<ClCompile Include="Connector\Interface\Browser\Bridge\Selection\Arg\SelectionInfo.cpp">
<Filter>Connector\Interface\Browser\Bridge\Selection\Arg</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Connector\ConnectorResource.h">
@@ -333,5 +378,38 @@
<ClInclude Include="Connector\Interface\Browser\Bridge\Base\UpdateModel.h">
<Filter>Connector\Interface\Browser\Bridge\Base</Filter>
</ClInclude>
<ClInclude Include="Connector\Record\Collection\RecordCollection.h">
<Filter>Connector\Record\Collection</Filter>
</ClInclude>
<ClInclude Include="Connector\Interface\Browser\Bridge\Send\Arg\ConversionResult.h">
<Filter>Connector\Interface\Browser\Bridge\Send\Arg</Filter>
</ClInclude>
<ClInclude Include="Connector\Interface\Browser\Bridge\Send\Arg\SendError.h">
<Filter>Connector\Interface\Browser\Bridge\Send\Arg</Filter>
</ClInclude>
<ClInclude Include="Connector\Interface\Browser\Bridge\Send\Arg\SendObject.h">
<Filter>Connector\Interface\Browser\Bridge\Send\Arg</Filter>
</ClInclude>
<ClInclude Include="Connector\Interface\Browser\Bridge\Send\Arg\SendViaBrowserArgs.h">
<Filter>Connector\Interface\Browser\Bridge\Send\Arg</Filter>
</ClInclude>
<ClInclude Include="Connector\Interface\Browser\Bridge\Send\GetSendSettings.h">
<Filter>Connector\Interface\Browser\Bridge\Send</Filter>
</ClInclude>
<ClInclude Include="Connector\Record\Collection\FinishProxy.h">
<Filter>Connector\Record\Collection</Filter>
</ClInclude>
<ClInclude Include="Connector\Record\Collection\ProjectCollection.h">
<Filter>Connector\Record\Collection</Filter>
</ClInclude>
<ClInclude Include="Connector\Interface\Browser\Bridge\Selection\GetSelection.h">
<Filter>Connector\Interface\Browser\Bridge\Selection</Filter>
</ClInclude>
<ClInclude Include="Connector\Interface\Browser\Bridge\Selection\SelectionBridge.h">
<Filter>Connector\Interface\Browser\Bridge\Selection</Filter>
</ClInclude>
<ClInclude Include="Connector\Interface\Browser\Bridge\Selection\Arg\SelectionInfo.h">
<Filter>Connector\Interface\Browser\Bridge\Selection\Arg</Filter>
</ClInclude>
</ItemGroup>
</Project>
@@ -29,7 +29,14 @@
215F082E2C94C5C000CD343B /* FilterMover.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 215F082C2C94C5C000CD343B /* FilterMover.cpp */; };
215F08372C95808B00CD343B /* ReceiverModelCard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 215F08362C95808B00CD343B /* ReceiverModelCard.cpp */; };
215F08462C9633A800CD343B /* EverythingSendFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 215F08452C9633A800CD343B /* EverythingSendFilter.cpp */; };
2192460D2CA3469D00CF5703 /* ProjectCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2192460B2CA3469D00CF5703 /* ProjectCollection.cpp */; };
219F30422C769283009834E9 /* ConfigTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 219F30402C769282009834E9 /* ConfigTests.cpp */; };
21A0FB982CB723240023F24E /* FinishProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21A0FB942CB723240023F24E /* FinishProxy.cpp */; };
21AEF9EB2CAB56E5000B8681 /* SendError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9E32CAB56E5000B8681 /* SendError.cpp */; };
21AEF9EC2CAB56E5000B8681 /* SendViaBrowserArgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9E52CAB56E5000B8681 /* SendViaBrowserArgs.cpp */; };
21AEF9EF2CAB5720000B8681 /* SendObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9EE2CAB5720000B8681 /* SendObject.cpp */; };
21AEF9FA2CAC3897000B8681 /* ConversionResult.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9F92CAC3897000B8681 /* ConversionResult.cpp */; };
21AEF9FD2CAD3FD8000B8681 /* GetSendSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9FB2CAD3FD8000B8681 /* GetSendSettings.cpp */; };
21B67CA32C769CB400FD64FC /* libActiveLib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 21F69EF52C64FE91008B6A06 /* libActiveLib.a */; };
21B67CA42C769CB400FD64FC /* libArchicad27.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 21F69ECD2C64C035008B6A06 /* libArchicad27.a */; };
21B67CAC2C77329800FD64FC /* BaseBridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21B67CA52C77329800FD64FC /* BaseBridge.cpp */; };
@@ -220,6 +227,7 @@
21F69F8D2C70D7EE008B6A06 /* GetAccounts.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69F8B2C70D7EE008B6A06 /* GetAccounts.cpp */; };
21F69FBB2C762EF0008B6A06 /* ConfigBridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69FB42C762EF0008B6A06 /* ConfigBridge.cpp */; };
21F69FBC2C762EF0008B6A06 /* GetConfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69FB62C762EF0008B6A06 /* GetConfig.cpp */; };
21FF70492CA1A7F400AAD99A /* RecordCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21FF70462CA1A7F400AAD99A /* RecordCollection.cpp */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -298,10 +306,24 @@
215F08452C9633A800CD343B /* EverythingSendFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EverythingSendFilter.cpp; sourceTree = "<group>"; };
215F084A2C9782F100CD343B /* ArchicadEverythingFilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ArchicadEverythingFilter.h; sourceTree = "<group>"; };
2161FD902BF2600C006D9527 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
2192460B2CA3469D00CF5703 /* ProjectCollection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProjectCollection.cpp; sourceTree = "<group>"; };
2192460C2CA3469D00CF5703 /* ProjectCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProjectCollection.h; sourceTree = "<group>"; };
219388682C4E5DE2002A0180 /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; };
219F30352C768F0A009834E9 /* Connector-AC27-Test.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Connector-AC27-Test.bundle"; sourceTree = BUILT_PRODUCTS_DIR; };
219F30402C769282009834E9 /* ConfigTests.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = ConfigTests.cpp; sourceTree = "<group>"; };
219F30432C7693B6009834E9 /* Connector-AC27-Debug.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = "Connector-AC27-Debug.xctestplan"; sourceTree = SOURCE_ROOT; };
21A0FB942CB723240023F24E /* FinishProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FinishProxy.cpp; sourceTree = "<group>"; };
21A0FB972CB723240023F24E /* FinishProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FinishProxy.h; sourceTree = "<group>"; };
21AEF9E32CAB56E5000B8681 /* SendError.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SendError.cpp; sourceTree = "<group>"; };
21AEF9E42CAB56E5000B8681 /* SendError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SendError.h; sourceTree = "<group>"; };
21AEF9E52CAB56E5000B8681 /* SendViaBrowserArgs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SendViaBrowserArgs.cpp; sourceTree = "<group>"; };
21AEF9E62CAB56E5000B8681 /* SendViaBrowserArgs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SendViaBrowserArgs.h; sourceTree = "<group>"; };
21AEF9ED2CAB5720000B8681 /* SendObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SendObject.h; sourceTree = "<group>"; };
21AEF9EE2CAB5720000B8681 /* SendObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SendObject.cpp; sourceTree = "<group>"; };
21AEF9F82CAC3897000B8681 /* ConversionResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConversionResult.h; sourceTree = "<group>"; };
21AEF9F92CAC3897000B8681 /* ConversionResult.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConversionResult.cpp; sourceTree = "<group>"; };
21AEF9FB2CAD3FD8000B8681 /* GetSendSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GetSendSettings.cpp; sourceTree = "<group>"; };
21AEF9FC2CAD3FD8000B8681 /* GetSendSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GetSendSettings.h; sourceTree = "<group>"; };
21B67CA52C77329800FD64FC /* BaseBridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BaseBridge.cpp; sourceTree = "<group>"; };
21B67CA62C77329800FD64FC /* BaseBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseBridge.h; sourceTree = "<group>"; };
21B67CA72C77329800FD64FC /* GetSourceApplicationName.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GetSourceApplicationName.cpp; sourceTree = "<group>"; };
@@ -534,6 +556,8 @@
21F69FB72C762EF0008B6A06 /* GetConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GetConfig.h; sourceTree = "<group>"; };
21F69FBD2C7630B3008B6A06 /* UpdateConfig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UpdateConfig.cpp; sourceTree = "<group>"; };
21F69FBE2C7630B3008B6A06 /* UpdateConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UpdateConfig.h; sourceTree = "<group>"; };
21FF70462CA1A7F400AAD99A /* RecordCollection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RecordCollection.cpp; sourceTree = "<group>"; };
21FF70472CA1A7F400AAD99A /* RecordCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecordCollection.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -939,6 +963,21 @@
path = ConnectorTests;
sourceTree = "<group>";
};
21AEF9E72CAB56E5000B8681 /* Arg */ = {
isa = PBXGroup;
children = (
21AEF9F92CAC3897000B8681 /* ConversionResult.cpp */,
21AEF9F82CAC3897000B8681 /* ConversionResult.h */,
21AEF9E32CAB56E5000B8681 /* SendError.cpp */,
21AEF9E42CAB56E5000B8681 /* SendError.h */,
21AEF9EE2CAB5720000B8681 /* SendObject.cpp */,
21AEF9ED2CAB5720000B8681 /* SendObject.h */,
21AEF9E52CAB56E5000B8681 /* SendViaBrowserArgs.cpp */,
21AEF9E62CAB56E5000B8681 /* SendViaBrowserArgs.h */,
);
path = Arg;
sourceTree = "<group>";
};
21B67CAB2C77329800FD64FC /* Base */ = {
isa = PBXGroup;
children = (
@@ -1023,8 +1062,11 @@
21D0BD5D2C89BFEA0077E104 /* Send */ = {
isa = PBXGroup;
children = (
21AEF9E72CAB56E5000B8681 /* Arg */,
21D0BD962C8F13F30077E104 /* GetSendFilters.cpp */,
21D0BD952C8F13F30077E104 /* GetSendFilters.h */,
21AEF9FB2CAD3FD8000B8681 /* GetSendSettings.cpp */,
21AEF9FC2CAD3FD8000B8681 /* GetSendSettings.h */,
21D0BD8D2C8EE4490077E104 /* Send.cpp */,
21D0BD8A2C8EE4490077E104 /* Send.h */,
21D0BD5B2C89BFEA0077E104 /* SendBridge.cpp */,
@@ -1036,6 +1078,7 @@
21D0BDD82C9387E60077E104 /* Record */ = {
isa = PBXGroup;
children = (
21FF70482CA1A7F400AAD99A /* Collection */,
21D0BDD92C9387F70077E104 /* Model */,
);
path = Record;
@@ -1188,6 +1231,19 @@
path = Config;
sourceTree = "<group>";
};
21FF70482CA1A7F400AAD99A /* Collection */ = {
isa = PBXGroup;
children = (
21A0FB942CB723240023F24E /* FinishProxy.cpp */,
21A0FB972CB723240023F24E /* FinishProxy.h */,
21FF70462CA1A7F400AAD99A /* RecordCollection.cpp */,
21FF70472CA1A7F400AAD99A /* RecordCollection.h */,
2192460B2CA3469D00CF5703 /* ProjectCollection.cpp */,
2192460C2CA3469D00CF5703 /* ProjectCollection.h */,
);
path = Collection;
sourceTree = "<group>";
};
7EA5F91E157FA18400693CEA /* EN-GB */ = {
isa = PBXGroup;
children = (
@@ -1393,8 +1449,10 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
21AEF9FA2CAC3897000B8681 /* ConversionResult.cpp in Sources */,
21D0BDAB2C8F363E0077E104 /* CardSetting.cpp in Sources */,
21B67CE32C78D1FB00FD64FC /* SayHiArg.cpp in Sources */,
21AEF9EB2CAB56E5000B8681 /* SendError.cpp in Sources */,
215F08462C9633A800CD343B /* EverythingSendFilter.cpp in Sources */,
21F69FBB2C762EF0008B6A06 /* ConfigBridge.cpp in Sources */,
21F69F8A2C70D2C4008B6A06 /* AccountBridge.cpp in Sources */,
@@ -1403,16 +1461,21 @@
21B67CF72C78D4DE00FD64FC /* GetComplexType.cpp in Sources */,
21D0BDDC2C93897B0077E104 /* SenderModelCard.cpp in Sources */,
21B67CAE2C77329800FD64FC /* GetSourceApplicationVersion.cpp in Sources */,
21A0FB982CB723240023F24E /* FinishProxy.cpp in Sources */,
21B67CC32C77649F00FD64FC /* GetDocumentState.cpp in Sources */,
21D0BD602C89BFEA0077E104 /* SendBridge.cpp in Sources */,
21D0BD972C8F13F30077E104 /* GetSendFilters.cpp in Sources */,
21B67CAC2C77329800FD64FC /* BaseBridge.cpp in Sources */,
2192460D2CA3469D00CF5703 /* ProjectCollection.cpp in Sources */,
21D0BD6A2C8A0DB40077E104 /* GetIsDevMode.cpp in Sources */,
210CC8832C80E6A300610F58 /* TriggerEvent.cpp in Sources */,
21B67CEB2C78D27200FD64FC /* DocumentInfo.cpp in Sources */,
21B67CB92C774BFA00FD64FC /* GetConnectorVersion.cpp in Sources */,
21B67CD92C78C83800FD64FC /* TestBridge.cpp in Sources */,
21AEF9FD2CAD3FD8000B8681 /* GetSendSettings.cpp in Sources */,
214B7A372C764BCD00D586C1 /* UpdateConfig.cpp in Sources */,
21AEF9EC2CAB56E5000B8681 /* SendViaBrowserArgs.cpp in Sources */,
21FF70492CA1A7F400AAD99A /* RecordCollection.cpp in Sources */,
21B67CC02C775A0D00FD64FC /* GetDocumentInfo.cpp in Sources */,
21D0BDD42C935D1A0077E104 /* UpdateModel.cpp in Sources */,
21B67CE72C78D23B00FD64FC /* ConnectorConfig.cpp in Sources */,
@@ -1422,6 +1485,7 @@
215F082A2C947F4400CD343B /* CardMover.cpp in Sources */,
215F08372C95808B00CD343B /* ReceiverModelCard.cpp in Sources */,
21D0BDD72C935DAE0077E104 /* RemoveModel.cpp in Sources */,
21AEF9EF2CAB5720000B8681 /* SendObject.cpp in Sources */,
21B67CDC2C78C88000FD64FC /* SayHi.cpp in Sources */,
215F082E2C94C5C000CD343B /* FilterMover.cpp in Sources */,
21F69F122C677BC0008B6A06 /* ConnectorMenu.cpp in Sources */,
+7 -7
View File
@@ -2,8 +2,8 @@
#include "ConnectorResource.h"
#include "Connector/Connector.h"
#include "Connector/Database/ModelCardDatabase.h"
#include "Interface/ConnectorMenu.h"
#include "Interface/ConnectorPalette.h"
#include "Connector/Interface/ConnectorMenu.h"
#include "Connector/Interface/ConnectorPalette.h"
#include "Speckle/Database/AccountDatabase.h"
#include "Speckle/Environment/Addon.h"
#include "Speckle/Utility/String.h"
@@ -35,16 +35,16 @@ namespace {
// MARK: Functions (const)
/*!
Get the model card database
@return The model card database
*/
const ModelCardDatabase* getModelCardDatabase() const override { return &m_modelCards; }
/*!
Get the account database
@return The account database
*/
const AccountDatabase* getAccountDatabase() const override;
/*!
Get the model card database
@return The model card database
*/
const ModelCardDatabase* getModelCardDatabase() const override { return &m_modelCards; }
private:
mutable std::unique_ptr<AccountDatabase> m_account;
+6 -5
View File
@@ -3,6 +3,7 @@
namespace speckle::database {
class AccountDatabase;
class BIMElementDatabase;
}
namespace connector::database {
class ModelCardDatabase;
@@ -16,16 +17,16 @@ namespace connector {
// MARK: Functions (const)
/*!
Get the model card database
@return The model card database
*/
const virtual database::ModelCardDatabase* getModelCardDatabase() const = 0;
/*!
Get the account database
@return The account database
*/
const virtual speckle::database::AccountDatabase* getAccountDatabase() const = 0;
/*!
Get the model card database
@return The model card database
*/
const virtual database::ModelCardDatabase* getModelCardDatabase() const = 0;
protected:
/*!
@@ -28,6 +28,7 @@ enum StringResource {
enum TitleString {
addonNameID = 1,
addonDescriptionID,
noStoreyID,
};
@@ -37,7 +38,7 @@ enum PromptString {
//Information strings (in UI content, logging, reports)
enum InfoString {
enum GeneralString {
};
@@ -53,6 +54,10 @@ enum WarningString {
//Error strings (errors displayed in alerts)
enum ErrorString {
noSelectedModelItemsID = 1,
modelCardNotFoundID,
noProjectOpenID,
accountNotFoundID,
};
#endif //CONNECTOR_RESOURCE
@@ -71,6 +71,16 @@ ModelCardDatabase::ModelCardDatabase() {
ModelCardDatabase::~ModelCardDatabase() {}
/*--------------------------------------------------------------------
Get a specified card from the database
return: The requested card (nullptr on failure)
--------------------------------------------------------------------*/
ModelCard::Unique ModelCardDatabase::getCard(const speckle::utility::String& cardID) const {
return m_store->getObject(cardID);
} //ModelCardDatabase::getCard
/*--------------------------------------------------------------------
Get all model cards
@@ -101,6 +111,16 @@ void ModelCardDatabase::erase(const String& cardID) const {
} //ModelCardDatabase::erase
/*--------------------------------------------------------------------
Get the unique ID of the engine storage
return: The databas unique ID
--------------------------------------------------------------------*/
RecordID ModelCardDatabase::getStoreID() const {
return m_engine->getUniqueID();
} //ModelCardDatabase::getStoreID
/*--------------------------------------------------------------------
Get a serialisation wrapper for the database
@@ -29,6 +29,11 @@ namespace connector::database {
// MARK: - Functions (const)
/*!
Get a specified card from the database
@return The requested card (nullptr on failure)
*/
record::ModelCard::Unique getCard(const speckle::utility::String& cardID) const;
/*!
Get all model cards
@return All the cards
@@ -49,6 +54,11 @@ namespace connector::database {
@return A database wrapper
*/
std::unique_ptr<active::serialise::Cargo> wrapper() const;
/*!
Get the unique ID of the engine storage
@return The database unique ID
*/
speckle::database::RecordID getStoreID() const;
// MARK: - Functions (mutating)
@@ -39,20 +39,20 @@ namespace connector::interfac::browser::bridge {
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
/*!
Set to the default package content
*/
Set to the default package content
*/
void setDefault() override;
};
@@ -2,6 +2,7 @@
#include "Active/Serialise/CargoHold.h"
#include "Connector/Connector.h"
#include "Connector/Database/ModelCardDatabase.h"
#include "Connector/Interface/Browser/Bridge/Base/Arg/DocumentInfo.h"
#include "Speckle/Environment/Project.h"
#include "Speckle/Utility/Guid.h"
@@ -37,7 +38,8 @@ std::unique_ptr<Cargo> GetDocumentInfo::run() const {
docInfo->name = info.name;
if (info.path)
docInfo->location = *info.path;
//TODO: No suitable project ID is currently available
if (auto cardDatabase = connector()->getModelCardDatabase(); cardDatabase != nullptr)
docInfo->ID = cardDatabase->getStoreID();
docInfo->ID = Guid{true}.operator String();
}
return std::make_unique<WrappedValue>(std::move(docInfo));
@@ -1,7 +1,6 @@
#include "Connector/Interface/Browser/Bridge/Base/GetDocumentState.h"
#include "Active/Serialise/CargoHold.h"
#include "Active/Serialise/Package/Wrapper/ContainerWrap.h"
#include "Connector/Connector.h"
#include "Connector/Record/Model/ModelCard.h"
#include "Connector/Database/ModelCardDatabase.h"
@@ -0,0 +1,92 @@
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include "Active/Serialise/Package/Wrapper/ContainerWrap.h"
#include "Connector/Connector.h"
#include "Connector/Interface/Browser/Bridge/Selection/Arg/SelectionInfo.h"
#include "Speckle/Database/BIMElementDatabase.h"
#include "Speckle/Environment/Project.h"
#include "Speckle/Record/Element/Element.h"
#include <array>
using namespace active::serialise;
using namespace connector::interfac::browser::bridge;
using namespace speckle::record::element;
namespace {
///Serialisation fields
enum FieldIndex {
selectedObjectIdsID,
summaryID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"selectedObjectIds"},
Identity{"summary"},
};
}
SelectionInfo::SelectionInfo() {
initialize();
}
void SelectionInfo::initialize() {
auto project = connector()->getActiveProject().lock();
if (!project) {
// TODO: is thi OK?
return;
}
auto elementDatabase = project->getElementDatabase();
auto selected = elementDatabase->getSelection();
active::utility::String summary(selected.size());
summary += " objects selected.";
m_summary = summary;
for (const auto& link : selected) {
m_selectedElementIds.push_back(link);
}
}
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool SelectionInfo::fillInventory(Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ fieldID[selectedObjectIdsID], selectedObjectIdsID, element },
{ fieldID[summaryID], summaryID, element },
},
}.withType(&typeid(SelectionInfo)));
return true;
} //SelectionInfo::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique SelectionInfo::getCargo(const Inventory::Item& item) const {
if (item.ownerType != &typeid(SelectionInfo))
return nullptr;
using namespace active::serialise;
switch (item.index) {
case selectedObjectIdsID:
return std::make_unique<ContainerWrap<std::vector<active::utility::Guid>>>(m_selectedElementIds);
case summaryID:
return std::make_unique<ValueWrap<active::utility::String>>(m_summary);
default:
return nullptr; //Requested an unknown index
}
} //SelectionInfo::getCargo
@@ -0,0 +1,49 @@
#ifndef CONNECTOR_INTERFACE_BRIDGE_SELECTION_INFO
#define CONNECTOR_INTERFACE_BRIDGE_SELECTION_INFO
#include "Active/Serialise/Package/Package.h"
namespace connector::interfac::browser::bridge {
/*!
Configuration settings class
*/
class SelectionInfo : public active::serialise::Package {
public:
// MARK: - Types
using base = active::serialise::Package;
// MARK: - Constructors
/*!
Default constructor
*/
SelectionInfo();
active::utility::String m_summary = "No objects selected";
std::vector<active::utility::Guid> m_selectedElementIds;
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
private:
void initialize();
};
}
#endif //CONNECTOR_INTERFACE_BRIDGE_SELECTION_INFO
@@ -0,0 +1,23 @@
#include "Active/Serialise/CargoHold.h"
#include "Connector/Interface/Browser/Bridge/Selection/GetSelection.h"
#include "Connector/Interface/Browser/Bridge/Selection/Arg/SelectionInfo.h"
using namespace active::serialise;
using namespace connector::interfac::browser::bridge;
/*--------------------------------------------------------------------
Default constructor
--------------------------------------------------------------------*/
GetSelection::GetSelection() : BridgeMethod{"GetSelection", [&]() {
return run();
}} {}
/*--------------------------------------------------------------------
Get the current selection info
based on the ArchiCAD mdoel selection
--------------------------------------------------------------------*/
std::unique_ptr<Cargo> GetSelection::run() const {
auto selectionInfo = std::make_unique<SelectionInfo>();
return std::make_unique<CargoHold<PackageWrap, SelectionInfo>>(std::move(selectionInfo));
} //GetSelection::run
@@ -0,0 +1,30 @@
#ifndef CONNECTOR_INTERFACE_BRIDGE_GETSELECTION
#define CONNECTOR_INTERFACE_BRIDGE_GETSELECTION
#include "Speckle/Interface/Browser/Bridge/BridgeMethod.h"
namespace connector::interfac::browser::bridge {
class GetSelection : public speckle::interfac::browser::bridge::BridgeMethod<void, active::serialise::Cargo> {
public:
// MARK: - Constructors
/*!
Constructor
@param bridge The parent bridge object (provides access to bridge methods)
*/
GetSelection();
// MARK: - Functions (const)
/*!
Get the current selection info
based on the ArchiCAD mdoel selection
*/
std::unique_ptr<active::serialise::Cargo> run() const;
};
}
#endif //CONNECTOR_INTERFACE_BRIDGE_GETSELECTION
@@ -0,0 +1,27 @@
#include "Active/Serialise/CargoHold.h"
#include "Connector/Interface/Browser/Bridge/Selection/SelectionBridge.h"
#include "Connector/Interface/Browser/Bridge/Selection/GetSelection.h"
#include "Connector/Interface/Browser/Bridge/Selection/Arg/SelectionInfo.h"
using namespace active::serialise;
using namespace connector::interfac::browser::bridge;
/*--------------------------------------------------------------------
Default constructor
--------------------------------------------------------------------*/
SelectionBridge::SelectionBridge() : BrowserBridge{"selectionBinding"} {
//Add bridge methods
addMethod<GetSelection>();
} //SelectionBridge::SelectionBridge
/*!
Handle the menu selection
@param event The selection event
@return True if the event should be closed
*/
bool SelectionBridge::handle(const speckle::event::SelectionEvent& event) {
auto selectionInfo = std::make_unique<SelectionInfo>();
auto wrapped = std::make_unique<CargoHold<PackageWrap, SelectionInfo>>(std::move(selectionInfo));
sendEvent("setSelection", std::move(wrapped));
return true;
} //SelectionBridge::handle
@@ -0,0 +1,39 @@
#ifndef CONNECTOR_INTERFACE_BRIDGE_SELECTION_BRIDGE
#define CONNECTOR_INTERFACE_BRIDGE_SELECTION_BRIDGE
#include "Speckle/Interface/Browser/Bridge/BrowserBridge.h"
#include "Speckle/Event/Subscriber/SelectionSubscriber.h"
#include "Speckle/Event/Type/SelectionEvent.h"
namespace connector::interfac::browser::bridge {
/*!
A browser bridge to support sending model data to a Speckle server
*/
class SelectionBridge : public speckle::interfac::browser::bridge::BrowserBridge, public speckle::event::SelectionSubscriber {
public:
// MARK: - Types
using base = speckle::interfac::browser::bridge::BrowserBridge;
// MARK: - Constructors
using base::base;
/*!
Default constructor
*/
SelectionBridge();
protected:
/*!
Handle the menu selection
@param event The selection event
@return True if the event should be closed
*/
bool handle(const speckle::event::SelectionEvent& event) override;
};
}
#endif //CONNECTOR_INTERFACE_BRIDGE_SELECTION_BRIDGE
@@ -0,0 +1,65 @@
#include "Connector/Interface/Browser/Bridge/Send/Arg/ConversionResult.h"
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include <array>
using namespace active::serialise;
using namespace connector::interfac::browser::bridge;
using namespace speckle::utility;
namespace {
///Serialisation fields
enum FieldIndex {
errorID,
cardID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"error"},
Identity{"modelCardId"},
};
}
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool ConversionResult::fillInventory(active::serialise::Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ fieldID[errorID], errorID, element },
{ fieldID[cardID], cardID, element },
},
}.withType(&typeid(ConversionResult)));
return true;
} //ConversionResult::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique ConversionResult::getCargo(const active::serialise::Inventory::Item& item) const {
if (item.ownerType != &typeid(ConversionResult))
return nullptr;
using namespace active::serialise;
switch (item.index) {
case errorID:
return std::make_unique<ValueWrap<String>>(message);
case cardID:
return std::make_unique<ValueWrap<String>>(modelCardID);
default:
return nullptr; //Requested an unknown index
}
} //ConversionResult::getCargo
@@ -0,0 +1,68 @@
#ifndef CONNECTOR_INTERFACE_BRIDGE_SEND_CONVERSION_RESULT
#define CONNECTOR_INTERFACE_BRIDGE_SEND_CONVERSION_RESULT
#include "Active/Serialise/Package/Wrapper/PackageWrap.h"
#include "Connector/Interface/Browser/Bridge/Send/Arg/SendError.h"
#include "Speckle/Utility/String.h"
namespace connector::interfac::browser::bridge {
/*!
A send error to return to the JS in the event of an error
*/
class ConversionResult final : public active::serialise::Package {
public:
enum class Status {
success = 1,
info,
warning,
error,
};
// MARK: - Constructors
/*!
Constructor
@param errMess The error message
@param card The ID of the model card associated with the wrror
*/
ConversionResult(const speckle::utility::String& errMess, const speckle::utility::String& card) : message{errMess}, modelCardID{card} {}
// MARK: - Public variables
///The error message
speckle::utility::String message;
///The ID of the model card associated with the data
speckle::utility::String modelCardID;
///The element conversion status
Status status = Status::info;
///For receive conversion reports, this is the id of the speckle object. For send, it's the host app object id.
speckle::utility::String sourceId;
///For receive conversion reports, this is the type of the speckle object. For send, it's the host app object type.
speckle::utility::String sourceType;
///For receive conversion reports, this is the id of the host app object. For send, it's the speckle object id.
speckle::utility::String resultId;
///For receive conversion reports, this is the type of the host app object. For send, it's the speckle object type.
speckle::utility::String resultType;
///The exception (nullopt = no exception)
SendError::Option error;
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
};
}
#endif //CONNECTOR_INTERFACE_BRIDGE_SEND_CONVERSION_RESULT
@@ -0,0 +1,65 @@
#include "Connector/Interface/Browser/Bridge/Send/Arg/SendError.h"
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include <array>
using namespace active::serialise;
using namespace connector::interfac::browser::bridge;
using namespace speckle::utility;
namespace {
///Serialisation fields
enum FieldIndex {
errorID,
cardID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"error"},
Identity{"modelCardId"},
};
}
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool SendError::fillInventory(active::serialise::Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ fieldID[errorID], errorID, element },
{ fieldID[cardID], cardID, element },
},
}.withType(&typeid(SendError)));
return true;
} //SendError::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique SendError::getCargo(const active::serialise::Inventory::Item& item) const {
if (item.ownerType != &typeid(SendError))
return nullptr;
using namespace active::serialise;
switch (item.index) {
case errorID:
return std::make_unique<ValueWrap<String>>(message);
case cardID:
return std::make_unique<ValueWrap<String>>(modelCardID);
default:
return nullptr; //Requested an unknown index
}
} //SendError::getCargo
@@ -0,0 +1,54 @@
#ifndef CONNECTOR_INTERFACE_BRIDGE_SEND_ERROR
#define CONNECTOR_INTERFACE_BRIDGE_SEND_ERROR
#include "Active/Serialise/Package/Wrapper/PackageWrap.h"
#include "Speckle/Utility/String.h"
namespace connector::interfac::browser::bridge {
/*!
A send error to return to the JS in the event of an error
*/
class SendError final : public active::serialise::Package {
public:
// MARK: Types
///Optional
using Option = std::optional<SendError>;
// MARK: - Constructors
/*!
Constructor
@param errMess The error message
@param card The ID of the model card associated with the wrror
*/
SendError(const speckle::utility::String& errMess, const speckle::utility::String& card) : message{errMess}, modelCardID{card} {}
// MARK: - Public variables
///The error message
speckle::utility::String message;
///The ID of the model card associated with the data
speckle::utility::String modelCardID;
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
};
}
#endif //CONNECTOR_INTERFACE_BRIDGE_SEND_ERROR
@@ -0,0 +1,69 @@
#include "Connector/Interface/Browser/Bridge/Send/Arg/SendObject.h"
#include "Active/Serialise/Package/Wrapper/ContainerWrap.h"
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include <array>
using namespace active::serialise;
using namespace connector::interfac::browser::bridge;
using namespace speckle::serialise;
using namespace speckle::utility;
namespace {
///Serialisation fields
enum FieldIndex {
idID,
rootObjID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"id"},
Identity{"rootObject"},
};
}
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool SendObject::fillInventory(active::serialise::Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ fieldID[idID], idID, element },
{ fieldID[rootObjID], rootObjID, element },
},
}.withType(&typeid(SendObject)));
return true;
} //SendObject::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique SendObject::getCargo(const active::serialise::Inventory::Item& item) const {
if (item.ownerType != &typeid(SendObject))
return nullptr;
using namespace active::serialise;
switch (item.index) {
case idID:
return std::make_unique<StringWrap>(id);
case rootObjID:
if (m_object)
return std::make_unique<PackageWrap>(*m_object);
return std::make_unique<NullPackage>();
default:
return nullptr; //Requested an unknown index
}
} //SendObject::getCargo
@@ -0,0 +1,64 @@
#ifndef CONNECTOR_INTERFACE_BRIDGE_SEND_OBJECT
#define CONNECTOR_INTERFACE_BRIDGE_SEND_OBJECT
#include "Active/Serialise/CargoHold.h"
#include "Connector/Interface/Browser/Bridge/Config/Arg/ConnectorConfig.h"
#include "Speckle/Database/Content/Record.h"
#include "Speckle/Interface/Browser/Bridge/BridgeMethod.h"
namespace connector::interfac::browser::bridge {
/*!
Class defining the primary content of a send
*/
class SendObject final : public active::serialise::Package {
public:
using base = std::reference_wrapper<active::serialise::Package>;
// MARK: - Constructors
/*!
Default constructor
@param object The object to send
*/
SendObject(std::unique_ptr<speckle::database::Record> object) : m_object{std::move(object)} { id = m_object->getID(); }
// MARK: - Public variables
///The root object id which should be used for creating the version
speckle::utility::String id;
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
/*!
Use a manager in (de)serialisation processes
@param management The management to use
*/
void useManagement(active::serialise::Management* management) const override { m_object->useManagement(management); }
/*!
Get the cargo management
@return The active management
*/
active::serialise::Management* management() const override { return m_object->management(); }
private:
///The object to send
std::unique_ptr<speckle::database::Record> m_object;
};
}
#endif //CONNECTOR_INTERFACE_BRIDGE_SEND_OBJECT
@@ -0,0 +1,114 @@
#include "Connector/Interface/Browser/Bridge/Send/Arg/SendViaBrowserArgs.h"
#include "Connector/Record/Model/ModelCard.h"
#include "Speckle/Record/Credentials/Account.h"
#include <array>
using namespace active::serialise;
using namespace connector::record;
using namespace connector::interfac::browser::bridge;
using namespace speckle::database;
using namespace speckle::record::cred;
using namespace speckle::serialise;
using namespace speckle::utility;
namespace {
///Serialisation fields
enum FieldIndex {
cardID,
projID,
modID,
tokenID,
serverID,
accID,
messageID,
sendObjectID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"modelCardId"},
Identity{"projectId"},
Identity{"modelId"},
Identity{"token"},
Identity{"serverUrl"},
Identity{"accountId"},
Identity{"message"},
Identity{"sendObject"},
};
}
/*--------------------------------------------------------------------
Constructor
modelCard: The model card to populate into the send info for the browser
account: The account linked to the send
object: The object to be sent
--------------------------------------------------------------------*/
SendViaBrowserArgs::SendViaBrowserArgs(const ModelCard& modelCard, const Account& account, SendObject&& object) :
modelCardID(modelCard.getID()), projectID(modelCard.getProjectID()), modelID(modelCard.getModelID()), token{account.getToken()},
serverURL{account.getServerURL()}, accountID{account.getID()}, sendObject{std::move(object)} {
} //SendViaBrowserArgs::SendViaBrowserArgs
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool SendViaBrowserArgs::fillInventory(active::serialise::Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ fieldID[cardID], cardID, element },
{ fieldID[projID], projID, element },
{ fieldID[modID], modID, element },
{ fieldID[tokenID], tokenID, element },
{ fieldID[serverID], serverID, element },
{ fieldID[accID], accID, element },
{ fieldID[messageID], messageID, element },
{ fieldID[sendObjectID], sendObjectID, element },
},
}.withType(&typeid(SendViaBrowserArgs)));
return true;
} //SendViaBrowserArgs::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique SendViaBrowserArgs::getCargo(const active::serialise::Inventory::Item& item) const {
if (item.ownerType != &typeid(SendViaBrowserArgs))
return nullptr;
using namespace active::serialise;
switch (item.index) {
case cardID:
return std::make_unique<StringWrap>(modelCardID);
case projID:
return std::make_unique<StringWrap>(projectID);
case modID:
return std::make_unique<StringWrap>(modelID);
case tokenID:
return std::make_unique<StringWrap>(token);
case serverID:
return std::make_unique<StringWrap>(serverURL);
case accID:
return std::make_unique<StringWrap>(accountID);
case messageID:
return std::make_unique<StringWrap>(message);
case sendObjectID:
return std::make_unique<PackageWrap>(sendObject);
default:
return nullptr; //Requested an unknown index
}
} //SendViaBrowserArgs::getCargo
@@ -0,0 +1,89 @@
#ifndef CONNECTOR_INTERFACE_BRIDGE_SEND_VIA_BROWSER_ARGS
#define CONNECTOR_INTERFACE_BRIDGE_SEND_VIA_BROWSER_ARGS
#include "Active/Serialise/Package/Package.h"
#include "Active/Utility/String.h"
#include "Connector/Interface/Browser/Bridge/Send/Arg/ConversionResult.h"
#include "Connector/Interface/Browser/Bridge/Send/Arg/SendObject.h"
#include "Speckle/Database/Identity/RecordID.h"
namespace speckle::record::cred {
class Account;
}
namespace connector::record {
class ModelCard;
}
namespace connector::interfac::browser::bridge {
class ConnectorConfig;
/*!
A commit of a project version (model) to the Speckle server
An object of this type is prepared by the Send bridge method
*/
class SendViaBrowserArgs final : public active::serialise::Package {
public:
// MARK: - Constructors
/*!
Constructor
@param modelCard The model card to populate into the send info for the browser
@param account The account linked to the send
@param object The object to be sent
*/
SendViaBrowserArgs(const connector::record::ModelCard& modelCard, const speckle::record::cred::Account& account, SendObject&& object);
// MARK: - Public variables
///ID of the model card driving the send request
speckle::database::RecordID modelCardID;
///The source project ID (from the model card)
speckle::database::RecordID projectID;
///The model ID (from the model card)
speckle::database::RecordID modelID;
///The account token (from the user account info)
speckle::utility::String token;
///The server URL (from the user account info)
speckle::utility::String serverURL;
///The user account ID
speckle::utility::String accountID;
///The send message
speckle::utility::String message; //TODO: Clarify what this is used for
///The conversion report (summarising the conversion results on an element-by-element basis)
std::vector<ConversionResult> sendConversionResults;
///The commit content
SendObject sendObject;
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
/*!
Use a manager in (de)serialisation processes
@param management The management to use
*/
void useManagement(active::serialise::Management* management) const override { sendObject.useManagement(management); }
/*!
Get the cargo management
@return The active management
*/
active::serialise::Management* management() const override { return sendObject.management(); }
};
}
#endif //CONNECTOR_INTERFACE_BRIDGE_SEND_VIA_BROWSER_ARGS
@@ -0,0 +1,36 @@
#include "Connector/Interface/Browser/Bridge/Send/GetSendSettings.h"
#include "Active/Container/Vector.h"
#include "Active/Serialise/CargoHold.h"
#include "Active/Serialise/Package/Wrapper/ContainerWrap.h"
#include "Connector/Record/Model/CardSetting.h"
using namespace active::container;
using namespace active::serialise;
using namespace connector::record;
using namespace connector::interfac::browser::bridge;
using namespace speckle::utility;
namespace {
using WrappedValue = active::serialise::CargoHold<ContainerWrap<Vector<CardSetting>, PackageWrap>, Vector<CardSetting>>;
}
/*--------------------------------------------------------------------
Default constructor
--------------------------------------------------------------------*/
GetSendSettings::GetSendSettings() : BridgeMethod{"GetSendSettings", [&]() {
return run();
}} {}
/*--------------------------------------------------------------------
Get the send filters
return: The send filters
--------------------------------------------------------------------*/
std::unique_ptr<Cargo> GetSendSettings::run() const {
auto filters = std::make_unique<Vector<CardSetting>>();
return std::make_unique<WrappedValue>(std::move(filters));
} //GetSendSettings::run
@@ -0,0 +1,36 @@
#ifndef CONNECTOR_INTERFACE_BRIDGE_GET_SEND_SETTINGS
#define CONNECTOR_INTERFACE_BRIDGE_GET_SEND_SETTINGS
#include "Active/Serialise/CargoHold.h"
#include "Speckle/Interface/Browser/Bridge/BridgeMethod.h"
namespace connector::interfac::browser::bridge {
class ConnectorConfig;
/*!
JS Function class to retrieve the send filters
*/
class GetSendSettings : public speckle::interfac::browser::bridge::BridgeMethod<void, active::serialise::Cargo> {
public:
// MARK: - Constructors
/*!
Constructor
@param bridge The parent bridge object (provides access to bridge methods)
*/
GetSendSettings();
// MARK: - Functions (const)
/*!
Get the send filters
@return The send filters
*/
std::unique_ptr<active::serialise::Cargo> run() const;
};
}
#endif //CONNECTOR_INTERFACE_BRIDGE_GET_SEND_SETTINGS
@@ -2,12 +2,37 @@
#include "Active/Serialise/CargoHold.h"
#include "Active/Serialise/Package/Wrapper/PackageWrap.h"
#include "Connector/Connector.h"
#include "Connector/ConnectorResource.h"
#include "Connector/Database/ModelCardDatabase.h"
#include "Connector/Interface/Browser/Bridge/Send/Arg/SendError.h"
#include "Connector/Interface/Browser/Bridge/Send/Arg/SendViaBrowserArgs.h"
#include "Connector/Record/Collection/ProjectCollection.h"
#include "Speckle/Database/AccountDatabase.h"
#include "Speckle/Database/Content/BIMRecord.h"
#include "Speckle/Interface/Browser/Bridge/BrowserBridge.h"
#include "Speckle/Record/Credentials/Account.h"
#include "Speckle/Serialise/Detached/Storage/DetachedMemoryStore.h"
#include "Speckle/Utility/Exception.h"
#include "Speckle/Database/BIMElementDatabase.h"
#include "Speckle/Environment/Project.h"
#include "Speckle/Record/Element/Element.h"
using namespace speckle::record::element;
#include <array>
using namespace active::serialise;
using namespace connector::interfac::browser::bridge;
using namespace connector::record;
using namespace speckle::database;
using namespace speckle::serialise;
using namespace speckle::utility;
namespace {
}
/*--------------------------------------------------------------------
Default constructor
--------------------------------------------------------------------*/
@@ -19,9 +44,46 @@ Send::Send() : BridgeMethod{"Send", [&](const SendArgs& args) {
/*--------------------------------------------------------------------
Send a specified model
modelCardID: The ID of the madel to send
modelCardID: The ID of the model to send
--------------------------------------------------------------------*/
void Send::run(const String& modelCardID) const {
///TODO: Find and send selected elements - the following is a placeholder
throw Exception{"No objects were found to convert. Please update your publish filter!"};
//Find the specified model card
auto modelCardDatabase = connector()->getModelCardDatabase();
auto modelCard = modelCardDatabase->getCard(modelCardID);
if (!modelCard) {
getBridge()->sendEvent("setModelError",
std::make_unique<SendError>(connector()->getLocalString(errorString, modelCardNotFoundID), modelCardID));
return;
}
//Get the user account
auto accountDatabase = connector()->getAccountDatabase();
auto account = accountDatabase->getAccount(modelCard->getAccountID(), modelCard->getServerURL());
if (!account) {
getBridge()->sendEvent("setModelError",
std::make_unique<SendError>(connector()->getLocalString(errorString, accountNotFoundID), modelCardID));
return;
}
//Get the active project
auto project = connector()->getActiveProject().lock();
if (!project) {
getBridge()->sendEvent("setModelError",
std::make_unique<SendError>(connector()->getLocalString(errorString, noProjectOpenID), modelCardID));
return;
}
//Build a collection from the selected elements
auto collection = std::make_unique<ProjectCollection>(project);
auto elementDatabase = project->getElementDatabase();
auto selected = elementDatabase->getSelection();
if (selected.empty()) {
getBridge()->sendEvent("setModelError",
std::make_unique<SendError>(connector()->getLocalString(errorString, noSelectedModelItemsID), modelCardID));
return;
}
for (const auto& link : selected) {
if (auto element = elementDatabase->getElement(link); element)
collection->addElement(*element);
}
//Send the collected information
auto result = std::make_unique<SendViaBrowserArgs>(*modelCard, *account, SendObject{std::move(collection)});
getBridge()->sendEvent("sendByBrowser", std::move(result));
} //Send::run
@@ -33,7 +33,7 @@ namespace connector::interfac::browser::bridge {
/*!
Send a specified model
@param modelCardID The ID of the madel to send
@param modelCardID The ID of the model to send
*/
void run(const speckle::utility::String& modelCardID) const;
};
@@ -1,6 +1,7 @@
#include "Connector/Interface/Browser/Bridge/Send/SendBridge.h"
#include "Connector/Interface/Browser/Bridge/Send/GetSendFilters.h"
#include "Connector/Interface/Browser/Bridge/Send/GetSendSettings.h"
#include "Connector/Interface/Browser/Bridge/Send/Send.h"
using namespace connector::interfac::browser::bridge;
@@ -11,5 +12,6 @@ using namespace connector::interfac::browser::bridge;
SendBridge::SendBridge() : BrowserBridge{"sendBinding"} {
//Add bridge methods
addMethod<GetSendFilters>();
addMethod<GetSendSettings>();
addMethod<Send>();
} //SendBridge::SendBridge
@@ -1,12 +1,17 @@
#include "Connector/Interface/ConnectorPalette.h"
#include "Active/Event/Event.h"
#include "Active/Utility/String.h"
#include "Active/Serialise/JSON/JSONTransport.h"
#include "Active/Utility/BufferOut.h"
#include "Connector/Connector.h"
#include "Connector/ConnectorResource.h"
#include "Connector/Event/ConnectorEventID.h"
#include "Connector/Interface/Browser/Bridge/Account/AccountBridge.h"
#include "Connector/Interface/Browser/Bridge/Base/BaseBridge.h"
#include "Connector/Interface/Browser/Bridge/Config/ConfigBridge.h"
#include "Connector/Interface/Browser/Bridge/Send/SendBridge.h"
#include "Connector/Interface/Browser/Bridge/Selection/SelectionBridge.h"
#include "Connector/Interface/Browser/Bridge/Test/TestBridge.h"
#include "Speckle/Environment/Addon.h"
#include "Speckle/Event/Type/MenuEvent.h"
@@ -73,9 +78,6 @@ namespace {
virtual void PanelResized(const DG::PanelResizeEvent& ev) override;
virtual void PanelCloseRequested(const DG::PanelCloseRequestEvent& ev, bool* accepted) override;
static GS::Array<BrowserPalette::ElementInfo> GetSelectedElements();
static void ModifySelection(const GS::UniString& elemGuidStr, SelectionModification modification);
static GSErrCode __ACENV_CALL PaletteControlCallBack(Int32 paletteId, API_PaletteMessageID messageID, GS::IntPtr param);
static GS::Ref<BrowserPalette> instance;
@@ -167,6 +169,12 @@ BrowserPalette::BrowserPalette() :
install<BaseBridge>();
install<ConfigBridge>();
install<SendBridge>();
if (auto ref = install<SelectionBridge>(); ref) {
if (auto selectionBridgeRef = std::dynamic_pointer_cast<SelectionBridge>(ref); selectionBridgeRef) {
connector::connector()->addWeak(selectionBridgeRef);
selectionBridgeRef->start();
}
}
install<TestBridge>();
InitBrowserControl();
}
@@ -206,7 +214,8 @@ void BrowserPalette::Hide() {
void BrowserPalette::InitBrowserControl() {
#ifdef TESTING_MODE
browser->LoadURL("https://boisterous-douhua-e3cefb.netlify.app/test");
browser->LoadURL("https://deploy-preview-3180--boisterous-douhua-e3cefb.netlify.app/");
//browser->LoadURL("https://boisterous-douhua-e3cefb.netlify.app/test");
#else
browser->LoadURL("https://boisterous-douhua-e3cefb.netlify.app/");
#endif
@@ -239,31 +248,6 @@ void BrowserPalette::PanelCloseRequested(const DG::PanelCloseRequestEvent&, bool
*accepted = true;
}
GS::Array<BrowserPalette::ElementInfo> BrowserPalette::GetSelectedElements() {
API_SelectionInfo selectionInfo;
GS::Array<API_Neig> selNeigs;
ACAPI_Selection_Get(&selectionInfo, &selNeigs, false, false);
BMKillHandle((GSHandle*)&selectionInfo.marquee.coords);
GS::Array<BrowserPalette::ElementInfo> selectedElements;
for(const API_Neig& neig : selNeigs) {
API_Elem_Head elemHead = {};
elemHead.guid = neig.guid;
ACAPI_Element_GetHeader(&elemHead);
ElementInfo elemInfo;
elemInfo.guidStr = APIGuidToString(elemHead.guid);
ACAPI_Element_GetElemTypeName(elemHead.type, elemInfo.typeName);
ACAPI_Element_GetElementInfoString(&elemHead.guid, &elemInfo.elemID);
selectedElements.Push(elemInfo);
}
return selectedElements;
}
void BrowserPalette::ModifySelection(const GS::UniString& elemGuidStr, BrowserPalette::SelectionModification modification) {
ACAPI_Selection_Select({ API_Neig(APIGuidFromString(elemGuidStr.ToCStr().Get())) }, modification == AddToSelection);
}
GSErrCode __ACENV_CALL BrowserPalette::PaletteControlCallBack(Int32, API_PaletteMessageID messageID, GS::IntPtr param) {
switch(messageID) {
case APIPalMsg_OpenPalette:
@@ -0,0 +1,68 @@
#include "Connector/Record/Collection/FinishProxy.h"
#include "Active/Serialise/Package/Wrapper/ContainerWrap.h"
#include "Active/Serialise/Package/Wrapper/PackageWrap.h"
#include <array>
using namespace active::serialise;
using namespace connector::record;
using namespace speckle::utility;
namespace {
///Serialisation fields
enum FieldIndex {
materialID,
linkedMeshID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"value"},
Identity{"objects"},
};
}
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool FinishProxy::fillInventory(active::serialise::Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ fieldID[materialID], materialID, element },
{ fieldID[linkedMeshID], linkedMeshID, element },
},
}.withType(&typeid(FinishProxy)));
return base::fillInventory(inventory);
} //FinishProxy::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique FinishProxy::getCargo(const active::serialise::Inventory::Item& item) const {
if (item.ownerType != &typeid(FinishProxy))
return base::getCargo(item);
using namespace active::serialise;
switch (item.index) {
case materialID:
return std::make_unique<PackageWrap>(m_finish);
case linkedMeshID: {
auto result = new ContainerWrap(m_meshID);
return Cargo::Unique{result};
}
default:
return nullptr; //Requested an unknown index
}
} //FinishProxy::getCargo
@@ -0,0 +1,60 @@
#ifndef CONNECTOR_RECORD_COLLECTION_MATERIAL_PROXY
#define CONNECTOR_RECORD_COLLECTION_MATERIAL_PROXY
#include "Speckle/Database/Content/BIMRecord.h"
#include "Speckle/Record/Attribute/Finish.h"
#include "Speckle/Utility/String.h"
namespace connector::record {
/*!
A proxy record binding a surface finishes to meshes
*/
class FinishProxy : public speckle::database::BIMRecord {
public:
using base = speckle::database::BIMRecord;
// MARK: - Constructors
/*!
Constructor
@param finish The proxy surface finish
@param meshID The list of mesh IDs the finish is applied to
*/
FinishProxy(const speckle::record::attribute::Finish& finish, const std::unordered_set<active::utility::Guid>& meshID) :
base{speckle::utility::Guid{true}, speckle::utility::Guid{}, std::nullopt}, m_finish{finish} {
std::copy(meshID.begin(), meshID.end(), std::back_inserter(m_meshID));
}
/*!
Get the speckle type identifier
@return The speckle type (relevant objects should override as required, but "Base" is still considered a type on its own)
*/
speckle::utility::String getSpeckleType() const override { return "Objects.Other.RenderMaterialProxy"; }
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
private:
///The proxy surface finish
speckle::record::attribute::Finish m_finish;
///The list of mesh IDs the finish is applied to
std::vector<active::utility::Guid> m_meshID;
};
}
#endif //CONNECTOR_RECORD_COLLECTION_MATERIAL_PROXY
@@ -0,0 +1,198 @@
#include "Connector/Record/Collection/ProjectCollection.h"
#include "Active/Serialise/CargoHold.h"
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include "Active/Serialise/Management/Management.h"
#include "Active/Serialise/Package/Wrapper/PackageWrap.h"
#include "Connector/Connector.h"
#include "Connector/ConnectorResource.h"
#include "Connector/Record/Collection/FinishProxy.h"
#include "Speckle/Database/BIMAttributeDatabase.h"
#include "Speckle/Database/BIMElementDatabase.h"
#include "Speckle/Record/Element/Element.h"
#ifdef ARCHICAD
#include <ACAPinc.h>
#include <ModelMaterial.hpp>
#endif
using namespace active::serialise;
using namespace connector::record;
using namespace speckle::database;
using namespace speckle::record::attribute;
using namespace speckle::utility;
#ifdef ARCHICAD
namespace connector::record {
class ProjectCollection::FinishCache : public std::unordered_map<active::utility::Guid, Finish::Unique> {};
}
#endif
namespace {
///Serialisation fields
enum FieldIndex {
finishProxyID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"renderMaterialProxies"},
};
using WrappedProxy = CargoHold<PackageWrap, FinishProxy>;
}
/*--------------------------------------------------------------------
Constructor
project: The source project
--------------------------------------------------------------------*/
ProjectCollection::ProjectCollection(speckle::environment::Project::Shared project) : base{project->getInfo().name, project},
m_management{std::make_unique<Management>()} {
m_management->push_back(this);
m_finishes = std::make_unique<FinishCache>();
base::useManagement(m_management.get());
} //ProjectCollection::ProjectCollection
/*--------------------------------------------------------------------
Destructor
--------------------------------------------------------------------*/
ProjectCollection::~ProjectCollection() {
} //ProjectCollection::~ProjectCollection
/*--------------------------------------------------------------------
Add an element to the collection hierarchy
index The index of the element to add
return: True if the element was added (false typically means the element already exists)
--------------------------------------------------------------------*/
bool ProjectCollection::addElement(const speckle::database::BIMIndex& index) {
//Lookup the element in the element database of the active project
auto elementDbase = m_project->getElementDatabase();
if (elementDbase == nullptr)
return false;
if (auto element = elementDbase->getElement(index); element) {
addElement(*element); //Add the element to the collection hierarchy
return true;
}
return false;
} //ProjectCollection::addElement
/*--------------------------------------------------------------------
Add an element to the collection hierarchy
element: The element to add
return: True if the element was added (false typically means the element already exists)
--------------------------------------------------------------------*/
bool ProjectCollection::addElement(const speckle::record::element::Element& element) {
std::vector<String> collectionNames;
//The first collection hierarchy level is the element storey/level
auto storey = element.getStorey();
collectionNames.emplace_back(storey ? storey->getName() : connector()->getLocalString(titleString, noStoreyID));
//The next level is the name of the element type
collectionNames.emplace_back(element.getTypeName());
//Add any future levels here as required
RecordCollection* collection = this;
for (const auto& childName : collectionNames)
collection = collection->getChild(childName);
return collection->addIndex(BIMIndex{element.getBIMID(), element.getTableID()});
} //ProjectCollection::addElement
/*--------------------------------------------------------------------
Add a material proxy record to the collection
materialIndex: The index of the material to add
objectID: The object the material is applied to
return: True if the material proxy was added (false typically means the record already exists)
--------------------------------------------------------------------*/
bool ProjectCollection::addMaterialProxy(const speckle::database::BIMIndex& materialIndex, const speckle::database::BIMRecordID& objectID) {
auto iter = m_finishProxies.find(materialIndex);
if (iter == m_finishProxies.end())
iter = m_finishProxies.insert({materialIndex, {}}).first;
return iter->second.insert(objectID).second;
} //ProjectCollection::addMaterialProxy
#ifdef ARCHICAD
/*--------------------------------------------------------------------
Add a ModelerAPI material to the collection (NB: These are not persistent so need to be captured by this method)
material: A material
objectID: The object the material is applied to
return: True if the material proxy was added (false typically means the record already exists)
--------------------------------------------------------------------*/
bool ProjectCollection::addMaterialProxy(const ModelerAPI::Material& material, const speckle::database::BIMRecordID& objectID) {
auto finishID = Guid::fromInt(material.GenerateHashValue());
auto iter = m_finishes->find(finishID);
if (iter == m_finishes->end()) {
auto finish = std::make_unique<Finish>(material);
iter = m_finishes->insert({ finishID, std::move(finish) }).first;
}
return addMaterialProxy(finishID, objectID);
} //ProjectCollection::addMaterialProxy
#endif
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool ProjectCollection::fillInventory(active::serialise::Inventory& inventory) const {
using enum Entry::Type;
base::fillInventory(inventory);
inventory.merge(Inventory{
{
{ Identity{fieldID[finishProxyID]}, finishProxyID, 100, std::nullopt },
},
}.withType(&typeid(ProjectCollection)));
return true;
} //ProjectCollection::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique ProjectCollection::getCargo(const Inventory::Item& item) const {
if (item.ownerType != &typeid(ProjectCollection))
return base::getCargo(item);
using namespace active::serialise;
//TODO: This is only currently coded to write collection content - reading can be added as required in future
switch (item.index) {
case finishProxyID: {
if (item.available < m_finishProxies.size()) {
auto iter = m_finishProxies.begin();
std::advance(iter, item.available);
const Finish* finish = nullptr;
if (auto fin = m_finishes->find(iter->first); fin != m_finishes->end())
finish = fin->second.get();
else if (auto attribute = m_project->getAttributeDatabase()->getAttribute(iter->first, iter->first.tableID); attribute)
finish = dynamic_cast<const Finish*>(attribute.get());
if (finish != nullptr) {
auto proxy = std::make_unique<FinishProxy>(*finish, iter->second);
return std::make_unique<WrappedProxy>(std::move(proxy));
}
}
break;
}
default:
break;
}
return nullptr; //Requested an unknown index
} //ProjectCollection::getCargo
@@ -0,0 +1,113 @@
#ifndef CONNECTOR_RECORD_ROOT_COLLECTiON
#define CONNECTOR_RECORD_ROOT_COLLECTiON
#include "Connector/Record/Collection/RecordCollection.h"
#include "Speckle/Serialise/Collection/FinishCollector.h"
#include <stack>
namespace active::serialise {
class Management;
}
namespace speckle::record::element {
class Element;
}
namespace connector::record {
/*!
Root collection for sending a project model to a Speckle server
Additional information is anticipated at the root level that will not apply at any other level in the container hierarchy, e.g.:
- Classification hierarchy
- Layers
- Other attributes, e.g. materials
Add all this supplementary data to the root container as required
*/
class ProjectCollection : public RecordCollection, public speckle::serialise::FinishCollector {
public:
// MARK: - Types
using base = RecordCollection;
// MARK: - Constructors
/*!
Constructor
@param project The source project
*/
ProjectCollection(speckle::environment::Project::Shared project);
ProjectCollection(const ProjectCollection&) = delete;
/*!
Destructor
*/
~ProjectCollection();
using base::base;
// MARK: - Functions (const)
// MARK: - Functions (mutating)
/*!
Add an element to the collection hierarchy
@param index The index of the element to add
@return True if the element was added (false typically means the element already exists)
*/
bool addElement(const speckle::database::BIMIndex& index);
/*!
Add an element to the collection hierarchy
@param element The element to add
@return True if the element was added (false typically means the element already exists)
*/
bool addElement(const speckle::record::element::Element& element);
/*!
Add a material proxy record to the collection
@param materialIndex The index of the material to add
@param objectID The object the material is applied to
@return True if the material proxy was added (false typically means the record already exists)
*/
bool addMaterialProxy(const speckle::database::BIMIndex& materialIndex, const speckle::database::BIMRecordID& objectID) override;
#ifdef ARCHICAD
/*!
Add a ModelerAPI material to the collection (NB: These are not persistent so need to be captured by this method)
@param material A material
@param objectID The object the material is applied to
@return True if the material proxy was added (false typically means the record already exists)
*/
bool addMaterialProxy(const ModelerAPI::Material& material, const speckle::database::BIMRecordID& objectID) override;
#endif
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
active::serialise::Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
private:
using FinishProxies = std::unordered_map<speckle::database::BIMIndex, std::unordered_set<active::utility::Guid>>;
std::unique_ptr<active::serialise::Management> m_management;
///Finish proxies accumulated from meshes generated from the collection elements
FinishProxies m_finishProxies;
#ifdef ARCHICAD
class FinishCache;
///Finishes cached from ModelerAPI materials
std::unique_ptr<FinishCache> m_finishes;
#endif
};
}
#endif //CONNECTOR_RECORD_ROOT_COLLECTiON
@@ -0,0 +1,127 @@
#include "Connector/Record/Collection/RecordCollection.h"
#include "Active/Serialise/CargoHold.h"
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include "Active/Serialise/Package/Wrapper/PackageWrap.h"
#include "Speckle/Database/BIMElementDatabase.h"
using namespace active::serialise;
using namespace connector::record;
using namespace speckle::database;
using namespace speckle::environment;
using namespace speckle::record::element;
using namespace speckle::utility;
#include <array>
namespace {
///Serialisation fields
enum FieldIndex {
nameID,
elementID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"name"},
Identity{"elements"},
};
using WrappedElement = CargoHold<PackageWrap, Element>;
}
/*--------------------------------------------------------------------
Constructor
name: The collection name
project: The source project
--------------------------------------------------------------------*/
RecordCollection::RecordCollection(const speckle::utility::String& name, Project::Shared project) : m_name{name}, m_project{project} {
} //RecordCollection::RecordCollection
/*--------------------------------------------------------------------
Get a child collection by name (adding if missing)
name: The child name
return: A pointer to the requested child (nullptr on failure, caller does not take ownership)
--------------------------------------------------------------------*/
RecordCollection* RecordCollection::getChild(const speckle::utility::String& name) {
//Return an existing child if possible
if (auto iter = m_children.find(name); iter != m_children.end())
return &iter->second;
//Otherwise insert and return a new collection with the requested name
return &m_children.insert({name, RecordCollection{name, m_project}}).first->second;
} //RecordCollection::getChild
/*--------------------------------------------------------------------
Add an index to the collection
index: The index to add
return: True if the index was added (false typically means the index already exists)
--------------------------------------------------------------------*/
bool RecordCollection::addIndex(const speckle::database::BIMIndex& index) {
return m_indices.insert(index).second;
} //RecordCollection::addIndex
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool RecordCollection::fillInventory(active::serialise::Inventory& inventory) const {
using enum Entry::Type;
base::fillInventory(inventory);
inventory.merge(Inventory{
{
{ Identity{fieldID[nameID]}, nameID, element },
{ Identity{fieldID[elementID]}, elementID, m_children.size() + m_indices.size(), std::nullopt },
},
}.withType(&typeid(RecordCollection)));
return true;
} //RecordCollection::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique RecordCollection::getCargo(const Inventory::Item& item) const {
if (item.ownerType != &typeid(RecordCollection))
return base::getCargo(item);
using namespace active::serialise;
//TODO: This is only currently coded to write collection content - reading can be added as required in future
switch (item.index) {
case nameID:
return std::make_unique<StringWrap>(m_name);
case elementID: {
if (item.available < m_children.size()) {
auto iter = m_children.begin();
std::advance(iter, item.available);
return std::make_unique<PackageWrap>(iter->second);
}
auto index = item.available - m_children.size();
if (index < m_indices.size()) {
auto iter = m_indices.begin();
std::advance(iter, index);
if (auto element = m_project->getElementDatabase()->getElement(*iter, iter->tableID); element)
return std::make_unique<WrappedElement>(std::move(element));
}
break;
}
default:
break;
}
return nullptr; //Requested an unknown index
} //RecordCollection::getCargo
@@ -0,0 +1,122 @@
#ifndef CONNECTOR_RECORD_RECORD_COLLECTiON
#define CONNECTOR_RECORD_RECORD_COLLECTiON
#include "Speckle/Database/Content/Record.h"
#include "Speckle/Database/Identity/BIMIndex.h"
#include "Speckle/Environment/Project.h"
#include "Speckle/Utility/String.h"
#include <unordered_set>
#include <unordered_map>
namespace connector::record {
class ProjectCollection;
/*!
Container for a collection of elements (and potentially tables of associated attributes) for Speckle commits
The container only stores element indices - database operations (including serialisation) will lookup records from a specified BIMDatabase on
demand.
This container can used hierarchically, so an collection can be nested within another collection. The current structure is:
- Root
- Element containers dividing elements by level/storey
- Element containers dividing elements by classification
- [nested classification leaf nodes)
- Associated attributes, e.g. classification table (future)
Any level in the hierarchy may contain element indices, although this is currently unlikely at the root level (all elements have a level/storey)
Each container should be named appropriately, e.g. a level/storey collection should be named to match the level/storey.
Note that the serialisation is currently implemented for sending only. Receive can be added as required
*/
class RecordCollection : public speckle::database::Record {
public:
// MARK: - Types
using base = speckle::database::Record;
// MARK: - Constructors
/*!
Destructor
*/
~RecordCollection() {}
// MARK: - Functions (const)
/*!
Get the speckle type identifier
@return The speckle type (relevant objects should override as required, but "Base" is still considered a type on its own)
*/
speckle::utility::String getSpeckleType() const override { return "Speckle.Core.Models.Collections.Collection"; }
/*!
Get the container name
@return The container name
*/
const speckle::utility::String& getName() const { return m_name; }
/*!
Find a child by name
@param name The required child name
@return A pointer to the requested child (nullptr if not found)
*/
RecordCollection* findChild(const speckle::utility::String& name) const;
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
active::serialise::Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
private:
friend ProjectCollection;
// MARK: - Types
using Indices = std::unordered_set<speckle::database::BIMIndex>;
using Children = std::unordered_map<speckle::utility::String, RecordCollection>;
/*!
Constructor
@param name The collection name
@param project The source project
*/
RecordCollection(const speckle::utility::String& name, speckle::environment::Project::Shared project);
/*!
Get a child collection by name (adding if missing)
@param name The child name
@return A pointer to the requested child (nullptr on failure, caller does not take ownership)
*/
RecordCollection* getChild(const speckle::utility::String& name);
/*!
Add an index to the collection
@param index The index to add
@return True if the index was added (false typically means the index already exists)
*/
bool addIndex(const speckle::database::BIMIndex& index);
///The source project for the collection
speckle::environment::Project::Shared m_project;
///The collection name
speckle::utility::String m_name;
///Child nodes of this collection
Children m_children;
///Indices of records in this collection
Indices m_indices;
};
}
#endif //CONNECTOR_RECORD_RECORD_COLLECTiON
@@ -19,6 +19,8 @@ namespace connector::record {
// MARK: - Types
using base = speckle::database::Record;
///Unique pointer
using Unique = std::unique_ptr<ModelCard>;
//List of card settings
using SettingList = active::container::Vector<connector::record::CardSetting>;
@@ -67,6 +69,11 @@ namespace connector::record {
@return The setting type
*/
const speckle::utility::String& getAccountID() const { return m_accountID; }
/*!
Get the user commit message
@return The user commit message
*/
const speckle::utility::String& getMessage() const { return m_message; }
/*!
Get the setting type
@return The setting type
@@ -106,6 +113,8 @@ namespace connector::record {
speckle::database::RecordID m_accountID;
///The server URL
speckle::utility::String m_serverURL;
///The commit message from the user for the card
speckle::utility::String m_message;
///Settings for the model rendering, e.g. level of detail (LoD)
SettingList m_settings;
///True if the card has expired
+8
View File
@@ -1,4 +1,12 @@
'STR#' 32600 "Title strings" {
/* [ 1] */ "Speckle Connector"
/* [ 2] */ "Connector to share model content with Speckle"
/* [ 3] */ "No level"
}
'STR#' 32604 "Error strings" {
/* [ 1] */ "No objects were found to convert. Please update your publish filter!"
/* [ 2] */ "The specified model card cannot be found. Try another card or create a new one"
/* [ 3] */ "Please open a project first"
/* [ 4] */ "The specified Speckle account cannot be found. Check that you have logged into your Speckle account with the Speckle Manager"
}
+1
View File
@@ -1,3 +1,4 @@
'STR#' 32700 "Speckle Title strings" {
/* [ 1] */ "Untitled"
/* [ 2] */ "Unknown"
}
@@ -81,6 +81,27 @@ AccountDatabase::AccountDatabase(const active::file::Path& path) {
AccountDatabase::~AccountDatabase() {}
/*--------------------------------------------------------------------
Get a specified account. NB: The server URL is provided as a fallback for the search if the specified accountID is not found
accountID: The account ID (the primary search field)
serverURL: The server URL (a fallback search field if the account ID does not exist)
return: The requested account (nullptr on failure)
--------------------------------------------------------------------*/
std::unique_ptr<Account> AccountDatabase::getAccount(const String& accountID, const String& serverURL) const {
//First attempt to find a matching account ID
auto matchingAccount = m_store->getObjects([&accountID](const auto& acc) { return acc.getID() == accountID; });
if (!matchingAccount.empty())
return matchingAccount.release(matchingAccount.begin());
//Alternatively seek an account with a matching server URL
matchingAccount = m_store->getObjects([&serverURL](const auto& acc) { return acc.getServerURL() == serverURL; });
if (!matchingAccount.empty())
return matchingAccount.release(matchingAccount.begin());
return nullptr;
} //AccountDatabase::getAccount
/*--------------------------------------------------------------------
Get all accounts
@@ -4,6 +4,10 @@
#include "Active/File/Path.h"
#include "Speckle/Record/Credentials/Account.h"
namespace speckle::record::cred {
class Account;
}
namespace speckle::database {
/*!
@@ -27,6 +31,13 @@ namespace speckle::database {
// MARK: - Functions (const)
/*!
Get a specified account. NB: The server URL is provided as a fallback for the search if the specified accountID is not found
@param accountID The account ID (the primary search field)
@param serverURL The server URL (a fallback search field if the account ID does not exist)
@return The requested account (nullptr on failure)
*/
std::unique_ptr<record::cred::Account> getAccount(const speckle::utility::String& accountID, const speckle::utility::String& serverURL) const;
/*!
Get all accounts
@return All the accounts
@@ -0,0 +1,155 @@
#include "Speckle/Database/BIMAttributeDatabase.h"
#include "Active/Database/Storage/Storage.h"
#include "Active/Serialise/UnboxedTransport.h"
#include "Speckle/Database/Identity/RecordID.h"
#include "Speckle/Database/Storage/ArchicadDBase/Attribute/ArchicadAttributeDBaseEngine.h"
#include "Speckle/Record/Attribute/Attribute.h"
#include <array>
using namespace active::container;
using namespace active::database;
using namespace active::event;
using namespace active::serialise;
using namespace speckle::database;
using namespace speckle::record;
using namespace speckle::record::attribute;
using namespace speckle::database;
using namespace speckle::utility;
namespace speckle::database {
///Define other platform engines here as required
#ifdef ARCHICAD
using AttributeDatabaseEngine = ArchicadAttributeDBaseEngine;
#endif
///Attribute database engine declaration
class BIMAttributeDatabase::Engine : public AttributeDatabaseEngine {
using base = ArchicadAttributeDBaseEngine;
using base::base;
};
///Attribute database storage declaration
class BIMAttributeDatabase::Store : public Storage<Attribute, UnboxedTransport, BIMRecordID, BIMRecordID, BIMRecordID, BIMRecordID> {
using base = Storage<Attribute, UnboxedTransport, BIMRecordID, BIMRecordID, BIMRecordID, BIMRecordID>;
using base::base;
};
}
namespace {
///The database storage identifier for attributes
const char* attributeDBaseName = "speckle::database::BIMAttributeDatabase";
///The primary model table, e.g. floor plan in Archicad
const char* modelTableName = "Model";
}
/*--------------------------------------------------------------------
Constructor
--------------------------------------------------------------------*/
BIMAttributeDatabase::BIMAttributeDatabase() {
m_engine = std::make_shared<Engine>(attributeDBaseName,
//Schema
DBaseSchema{active::utility::String{attributeDBaseName},
//Tables
{
//Model attribute table
{
modelTableName, 0, 0, {} //The primary model. Additonal tables could be linked to other drawings/layouts in future
}
}
}
);
m_store = std::make_shared<Store>(m_engine);
} //BIMAttributeDatabase::BIMAttributeDatabase
/*--------------------------------------------------------------------
Destructor
--------------------------------------------------------------------*/
BIMAttributeDatabase::~BIMAttributeDatabase() {}
/*--------------------------------------------------------------------
Get a specified attribute
attributeID: The ID of the target attribute
return: The requested attribute (nullptr on failure)
--------------------------------------------------------------------*/
Attribute::Unique BIMAttributeDatabase::getAttribute(const BIMRecordID& attributeID, std::optional<BIMRecordID> tableID,
std::optional<BIMRecordID> documentID) const {
return m_engine->getObject(attributeID, tableID, documentID);
} //BIMAttributeDatabase::getAttribute
/*--------------------------------------------------------------------
Get all attributes
return: All the attributes
--------------------------------------------------------------------*/
Vector<Attribute> BIMAttributeDatabase::getAttributes() const {
return m_store->getObjects();
} //BIMAttributeDatabase::getAttributes
/*--------------------------------------------------------------------
Write an attribute to storage
attribute: The attribute to write
--------------------------------------------------------------------*/
void BIMAttributeDatabase::write(const Attribute& attribute) const {
m_store->write(attribute);
} //BIMAttributeDatabase::write
/*--------------------------------------------------------------------
Erase an attribute
attributeID: The ID of the attribute to erase
--------------------------------------------------------------------*/
void BIMAttributeDatabase::erase(const Guid& attributeID) const {
m_store->erase(attributeID);
} //BIMAttributeDatabase::erase
#ifdef ARCHICAD
/*--------------------------------------------------------------------
Get attribute data direct from the AC API. For internal use - avoid direct use
link: A link to the required attribute
return: The AC API attribute data
--------------------------------------------------------------------*/
std::optional<API_Attribute> BIMAttributeDatabase::getAPIData(const BIMLink& link) const {
return m_engine->getAPIData(link);
} //BIMAttributeDatabase::getAPIData
/*--------------------------------------------------------------------
Get storey data direct from the AC API. For internal use - avoid direct use
link: A link to the required storey
return: The AC API storey data
--------------------------------------------------------------------*/
std::optional<API_StoryType> BIMAttributeDatabase::getAPIStorey(const BIMLink& link) const {
return m_engine->getAPIStorey(link);
} //BIMAttributeDatabase::getAPIData
/*--------------------------------------------------------------------
Get the ID of a storey from a specified index
index: The storey index
return: The storey ID (nullopt on failure)
--------------------------------------------------------------------*/
std::optional<BIMRecordID> BIMAttributeDatabase::getStoreyID(short index) const {
return m_engine->getStoreyID(index);
} //BIMAttributeDatabase::getStoreyID
#endif
@@ -0,0 +1,101 @@
#ifndef CONNECTOR_DATABASE_BIM_ATTRIBUTE_DATABASE
#define CONNECTOR_DATABASE_BIM_ATTRIBUTE_DATABASE
#include "Speckle/Database/Identity/BIMLink.h"
#include "Speckle/Record/Attribute/Attribute.h"
#include "Speckle/Utility/Guid.h"
namespace active::event {
class Subscriber;
}
namespace speckle::database {
/*!
Database of model attributes relating to a specific project
*/
class BIMAttributeDatabase {
public:
// MARK: - Constructors
/*!
Constructor
*/
BIMAttributeDatabase();
BIMAttributeDatabase(const BIMAttributeDatabase&) = delete;
/*!
Destructor
*/
~BIMAttributeDatabase();
// MARK: - Functions (const)
/*!
Get the current user attribute selection
@return A list of selected attribute IDs
*/
BIMLinkList getSelection() const;
/*!
Get a specified attribute
@param attributeID The ID of the target attribute
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (when the object is bound to a specific document)
@return The requested attribute (nullptr on failure)
*/
record::attribute::Attribute::Unique getAttribute(const BIMRecordID& attributeID, std::optional<BIMRecordID> tableID = std::nullopt,
std::optional<BIMRecordID> documentID = std::nullopt) const;
/*!
Get a specified attribute
@param link A link to the target attribute
@return The requested attribute (nullptr on failure)
*/
record::attribute::Attribute::Unique getAttribute(const BIMLink& link) const { return getAttribute(link, link.tableID, link.docID); }
/*!
Get all model attributes
@return All the attributes
*/
active::container::Vector<record::attribute::Attribute> getAttributes() const;
/*!
Write an attribute to storage
@param attribute The attribute to write
*/
void write(const record::attribute::Attribute& attribute) const;
/*!
Erase an attribute
@param attributeID The ID of the attribute to erase
*/
void erase(const speckle::utility::Guid& attributeID) const;
#ifdef ARCHICAD
/*!
Get attribute data direct from the AC API. For internal use - avoid direct use
@param link A link to the required attribute
@return The AC API attribute data
*/
std::optional<API_Attribute> getAPIData(const BIMLink& link) const;
/*!
Get storey data direct from the AC API. For internal use - avoid direct use
@param link A link to the required storey
@return The AC API storey data
*/
std::optional<API_StoryType> getAPIStorey(const BIMLink& link) const;
/*!
Get the ID of a storey from a specified index
@param index The storey index
@return The storey ID (nullopt on failure)
*/
std::optional<BIMRecordID> getStoreyID(short index) const;
#endif
private:
class Engine;
class Store;
///Model attribute database storage
std::shared_ptr<Engine> m_engine;
std::shared_ptr<Store> m_store;
};
}
#endif //CONNECTOR_DATABASE_BIM_ATTRIBUTE_DATABASE
@@ -0,0 +1,147 @@
#include "Speckle/Database/BIMElementDatabase.h"
#include "Active/Database/Storage/Storage.h"
#include "Active/Serialise/UnboxedTransport.h"
#include "Speckle/Database/Identity/RecordID.h"
#include "Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.h"
#include "Speckle/Record/Element/Element.h"
#include "Speckle/Record/Element/Memo.h"
#include <array>
using namespace active::container;
using namespace active::database;
using namespace active::event;
using namespace active::serialise;
using namespace speckle::database;
using namespace speckle::record;
using namespace speckle::record::element;
using namespace speckle::database;
using namespace speckle::utility;
namespace speckle::database {
///Define other platform engines here as required
#ifdef ARCHICAD
using ElementDatabaseEngine = ArchicadElementDBaseEngine;
#endif
///Element database engine declaration
class BIMElementDatabase::Engine : public ElementDatabaseEngine {
using base = ArchicadElementDBaseEngine;
using base::base;
};
///Element database storage declaration
class BIMElementDatabase::Store : public Storage<Element, UnboxedTransport, BIMRecordID, BIMRecordID, BIMRecordID, BIMRecordID> {
using base = Storage<Element, UnboxedTransport, BIMRecordID, BIMRecordID, BIMRecordID, BIMRecordID>;
using base::base;
};
}
namespace {
///The database storage identifier for elements
const char* elementDBaseName = "speckle::database::BIMElementDatabase";
///The primary model table, e.g. floor plan in Archicad
const char* modelTableName = "Model";
}
/*--------------------------------------------------------------------
Constructor
--------------------------------------------------------------------*/
BIMElementDatabase::BIMElementDatabase() {
m_engine = std::make_shared<Engine>(elementDBaseName,
//Schema
DBaseSchema{active::utility::String{elementDBaseName},
//Tables
{
//Model element table
{
modelTableName, 0, 0, {} //The primary model. Additonal tables could be linked to other drawings/layouts in future
}
}
}
);
m_store = std::make_shared<Store>(m_engine);
} //BIMElementDatabase::BIMElementDatabase
/*--------------------------------------------------------------------
Destructor
--------------------------------------------------------------------*/
BIMElementDatabase::~BIMElementDatabase() {}
/*--------------------------------------------------------------------
Get the current user element selection
return: A list of selected element IDs
--------------------------------------------------------------------*/
BIMLinkList BIMElementDatabase::getSelection() const {
return m_engine->getSelection();
} //BIMElementDatabase::getSelection
/*--------------------------------------------------------------------
Get a specified element
elementID: The ID of the target element
return: The requested element (nullptr on failure)
--------------------------------------------------------------------*/
Element::Unique BIMElementDatabase::getElement(const BIMRecordID& elementID, std::optional<BIMRecordID> tableID,
std::optional<BIMRecordID> documentID) const {
return m_engine->getObject(elementID, tableID, documentID);
} //BIMElementDatabase::getElement
/*--------------------------------------------------------------------
Get all elements
return: All the elements
--------------------------------------------------------------------*/
Vector<Element> BIMElementDatabase::getElements() const {
return m_store->getObjects();
} //BIMElementDatabase::getElements
/*--------------------------------------------------------------------
Get memo memo (supplementary) data for a specified element
elementID: The of the source element
filter: Filter for the required supplementary data
return: The requested element memo data (nullptr on failure)
--------------------------------------------------------------------*/
Memo::Unique BIMElementDatabase::getMemo(const BIMRecordID& elementID, Part::filter_bits filter) const {
//NB: The filter bits are passed as the source document ID
auto result = m_engine->getObject(elementID, ArchicadElementDBaseEngine::memoTable, Guid::fromInt(filter));
if (auto memo = dynamic_cast<Memo*>(result.get()); memo != nullptr) {
result.release();
return Memo::Unique{memo};
}
return nullptr;
} //BIMElementDatabase::getMemo
/*--------------------------------------------------------------------
Write an element to storage
element: The element to write
--------------------------------------------------------------------*/
void BIMElementDatabase::write(const Element& element) const {
m_store->write(element);
} //BIMElementDatabase::write
/*--------------------------------------------------------------------
Erase an element
elementID: The ID of the element to erase
--------------------------------------------------------------------*/
void BIMElementDatabase::erase(const Guid& elementID) const {
m_store->erase(elementID);
} //BIMElementDatabase::erase
@@ -0,0 +1,92 @@
#ifndef CONNECTOR_DATABASE_BIM_ELEMENT_DATABASE
#define CONNECTOR_DATABASE_BIM_ELEMENT_DATABASE
#include "Speckle/Database/Identity/BIMLink.h"
#include "Speckle/Record/Element/Element.h"
#include "Speckle/Record/Element/Interface/Part.h"
#include "Speckle/Utility/Guid.h"
namespace active::event {
class Subscriber;
}
namespace speckle::record::element {
class Memo;
}
namespace speckle::database {
/*!
Database of model elements relating to a specific project
*/
class BIMElementDatabase {
public:
// MARK: - Constructors
/*!
Constructor
*/
BIMElementDatabase();
BIMElementDatabase(const BIMElementDatabase&) = delete;
/*!
Destructor
*/
~BIMElementDatabase();
// MARK: - Functions (const)
/*!
Get the current user element selection
@return A list of selected element IDs
*/
BIMLinkList getSelection() const;
/*!
Get a specified element
@param elementID The ID of the target element
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (when the object is bound to a specific document)
@return The requested element (nullptr on failure)
*/
std::unique_ptr<record::element::Element> getElement(const BIMRecordID& elementID, std::optional<BIMRecordID> tableID = std::nullopt,
std::optional<BIMRecordID> documentID = std::nullopt) const;
/*!
Get a specified element
@param link A link to the target element
@return The requested element (nullptr on failure)
*/
std::unique_ptr<record::element::Element> getElement(const BIMLink& link) const { return getElement(link, link.tableID, link.docID); }
/*!
Get all model elements
@return All the elements
*/
active::container::Vector<record::element::Element> getElements() const;
/*!
Get memo memo (supplementary) data for a specified element
@param elementID The of the source element
@param filter Filter for the required supplementary data
@return The requested element memo data (nullptr on failure)
*/
std::unique_ptr<record::element::Memo> getMemo(const BIMRecordID& elementID, record::element::Part::filter_bits filter) const;
/*!
Write an element to storage
@param element The element to write
*/
void write(const record::element::Element& element) const;
/*!
Erase an element
@param elementID The ID of the element to erase
*/
void erase(const speckle::utility::Guid& elementID) const;
private:
class Engine;
class Store;
///Model element database storage
std::shared_ptr<Engine> m_engine;
std::shared_ptr<Store> m_store;
};
}
#endif //CONNECTOR_DATABASE_BIM_ELEMENT_DATABASE
@@ -0,0 +1,78 @@
#include "Speckle/Database/Content/BIMRecord.h"
#include "Active/Serialise/Item/Wrapper/ValueOptionWrap.h"
#include "Speckle/Serialise/Types/Units/LengthUnit.h"
#include "Speckle/Utility/Guid.h"
#include <array>
using namespace active::serialise;
using namespace speckle::database;
using namespace speckle::utility;
using enum active::measure::LengthType;
namespace {
///Serialisation fields
enum FieldIndex {
applicID,
unitID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"applicationId"},
Identity{"units"},
};
}
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool BIMRecord::fillInventory(Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ Identity{fieldID[applicID]}, applicID, element, !m_applicationID.empty() },
{ Identity{fieldID[unitID]}, unitID, element, m_unit.operator bool() },
},
}.withType(&typeid(BIMRecord)));
return base::fillInventory(inventory);
} //BIMRecord::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique BIMRecord::getCargo(const Inventory::Item& item) const {
if (item.ownerType != &typeid(BIMRecord))
return base::getCargo(item);
using namespace active::serialise;
switch (item.index) {
case applicID:
return std::make_unique<ValueWrap<BIMRecordID>>(m_applicationID);
case unitID:
return std::make_unique<ValueOptionWrap<active::measure::LengthType>>(m_unit);
default:
return nullptr; //Requested an unknown index
}
} //BIMRecord::getCargo
/*--------------------------------------------------------------------
Set to the default package content
--------------------------------------------------------------------*/
void BIMRecord::setDefault() {
m_applicationID.clear();
m_unit.reset();
} //BIMRecord::setDefault
@@ -0,0 +1,130 @@
#ifndef SPECKLE_DATABASE_BIM_RECORD
#define SPECKLE_DATABASE_BIM_RECORD
#include "Active/Setting/Values/Measurement/Units/LengthUnit.h"
#include "Speckle/Database/Content/Record.h"
#include "Speckle/Database/Identity/BIMIndex.h"
#include "Speckle/Database/Identity/BIMLink.h"
#include "Speckle/Database/Identity/BIMRecordID.h"
namespace speckle::database {
/*!
Base class for a database record
*/
class BIMRecord : public Record {
public:
// MARK: - Types
using base = Record;
///Unique pointer
using Unique = std::unique_ptr<BIMRecord>;
///Shared pointer
using Shared = std::shared_ptr<BIMRecord>;
///Optional
using Option = std::optional<BIMRecord>;
// MARK: - Constructors
/*!
Default constructor
@param unit The recordc unit type
*/
BIMRecord(active::measure::LengthType unit = active::measure::LengthType::metre) : base{}, m_unit{unit} {}
/*!
Constructor
@param ID The record ID
@param tableID The parent table ID
@param unit The record unit type
*/
BIMRecord(const speckle::utility::Guid& ID, const speckle::utility::Guid& tableID,
std::optional<active::measure::LengthType> unit = active::measure::LengthType::metre) :
base{}, m_applicationID{ID}, m_applicationTableID{tableID}, m_unit{unit} {}
/*!
Destructor
*/
virtual ~BIMRecord() {}
// MARK: - Functions (const)
/*!
Get the BIM application ID
@return The BIM application ID
*/
BIMRecordID getBIMID() const { return m_applicationID; }
/*!
Get the BIM application parent table ID
@return The BIM table ID
*/
BIMRecordID getTableID() const { return m_applicationTableID; }
/*!
Get a link to the BIM record
@return The BIM record link
*/
BIMLink getBIMLink() const { return BIMLink{ BIMLink::base{m_applicationID, m_applicationTableID} }; }
/*!
Get the record unit type
@return The record unit type (nullopt if the record has no applicable unit type)
*/
std::optional<active::measure::LengthType> getUnit() const { return m_unit; }
// MARK: - Functions (mutating)
/*!
Set the BIM application ID
@param ID The BIM application ID
*/
void setBIMID(const BIMRecordID& ID) { m_applicationID = ID; }
/*!
Set the BIM application parent table ID
@param tableID The BIM table ID
*/
void setTableID(const BIMRecordID& tableID) { m_applicationTableID = tableID; }
/*!
Set the record unit type
@param unit The record unit type (nullopt if the record has no applicable unit type)
*/
void setUnit(std::optional<active::measure::LengthType> unit) { m_unit = unit; }
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
active::serialise::Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
/*!
Set to the default package content
*/
void setDefault() override;
protected:
/*!
Reset the BIM index (used in lazy loading contexts where the index cannot otherwise be established)
@param index The BIM application index
*/
void resetIndex(const BIMIndex& index) const {
m_applicationID = index;
m_applicationTableID = index.tableID;
}
private:
///The BIM application record ID
mutable BIMRecordID m_applicationID;
///The BIM application parent table ID
mutable BIMRecordID m_applicationTableID;
///The BIM record unit of length measurement
std::optional<active::measure::LengthType> m_unit = active::measure::LengthType::metre;
};
}
#endif //SPECKLE_DATABASE_BIM_RECORD
+56 -1
View File
@@ -2,10 +2,28 @@
#include "Speckle/Utility/Guid.h"
#include <array>
using namespace active::serialise;
using namespace speckle::database;
using namespace speckle::utility;
namespace {
///Serialisation fields
enum FieldIndex {
speckleIDID,
speckleTypeID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"id"},
Identity{"speckle_type"},
};
}
/*--------------------------------------------------------------------
Fill an inventory with the package items
@@ -17,8 +35,45 @@ bool Record::fillInventory(active::serialise::Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ Identity{"id"}, active::database::record::FieldIndex::idIndex, element },
{ Identity{fieldID[speckleIDID]}, active::database::record::FieldIndex::idIndex, element, !getID().empty() },
},
}.withType(&typeid(base)));
inventory.merge(Inventory{
{
{ Identity{fieldID[speckleTypeID]}, speckleTypeID, attribute },
},
}.withType(&typeid(Record)));
return true;
} //Record::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique Record::getCargo(const Inventory::Item& item) const {
if (item.ownerType != &typeid(Record))
return base::getCargo(item);
using namespace active::serialise;
switch (item.index) {
case speckleTypeID:
//If the type is undefined, we're in a serialisation process and should populate it with whatever the object says it is
if (!m_type)
m_type = getSpeckleType();
return std::make_unique<StringWrap>(*m_type);
default:
return nullptr; //Requested an unknown index
}
} //Record::getCargo
/*--------------------------------------------------------------------
Set to the default package content
--------------------------------------------------------------------*/
void Record::setDefault() {
//Ensure the content starts with an empty (defined) string for deserialisation so we can discover the incoming type
m_type = String{};
} //Record::setDefault
+27 -12
View File
@@ -27,22 +27,23 @@ namespace speckle::database {
/*!
Default constructor
*/
Record() : base{active::utility::Guid{true}.operator active::utility::String(),
active::utility::Guid{true}.operator active::utility::String()} {} //TODO: Implement a better default for the ID
/*!
Constructor
@param ID The record ID
@param globID The global ID
*/
Record(speckle::utility::String ID, speckle::utility::String::Option globID = std::nullopt) :
base{ID, globID.value_or(active::utility::Guid{true}.operator active::utility::String())} {}
Record(speckle::utility::String::Option ID = std::nullopt, speckle::utility::String::Option globID = std::nullopt) :
base{ID.value_or(speckle::utility::String{}), globID.value_or(speckle::utility::String{})} {}
/*!
Destructor
*/
virtual ~Record() {}
// MARK: - Functions (const)
/*!
Get the speckle type identifier
@return The speckle type (relevant objects should override as required, but "Base" is still considered a type on its own)
*/
virtual speckle::utility::String getSpeckleType() const { return "speckle::database::Record"; }
// MARK: - Functions (mutating)
@@ -50,11 +51,25 @@ namespace speckle::database {
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
active::serialise::Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
/*!
Set to the default package content
*/
void setDefault() override;
private:
///Cache for the speckle type during serialisation operations
mutable speckle::utility::String::Option m_type;
};
}
@@ -0,0 +1,42 @@
#ifndef SPECKLE_DATABASE_BIM_INDEX
#define SPECKLE_DATABASE_BIM_INDEX
#include "Active/Database/Identity/Index.h"
#include "Speckle/Database/Identity/BIMRecordID.h"
namespace speckle::database {
/*!
Record index class
A record index is the information required to uniquely identify (and locate) a specified record in some database/app context. For Archicad,
this is typically a guid, for Revit a string and for Vectorworks a handle. Note that this index is not necessarily persistent between
sessions.
*/
class BIMIndex : public active::database::Index<BIMRecordID, BIMRecordID> {
public:
// MARK: - Types
using base = active::database::Index<BIMRecordID, BIMRecordID>;
// MARK: - Constructors
using base::base;
};
}
///Hashing for BIMIndex class, e.g. to use as a key in unordered_map
template<>
struct std::hash<speckle::database::BIMIndex> {
std::size_t operator() (const speckle::database::BIMIndex& index) const {
std::size_t h1 = std::hash<active::utility::Guid>{}(index);
std::size_t h2 = std::hash<active::utility::Guid>{}(index.tableID);
return h1 ^ (h2 << 1);
}
};
#endif //SPECKLE_DATABASE_BIM_INDEX
@@ -1,5 +1,4 @@
#include "Speckle/Database/Identity/Link.h"
#include "Speckle/Utility/Guid.h"
#include "Speckle/Database/Identity/BIMLink.h"
using namespace speckle::database;
using namespace speckle::utility;
@@ -9,10 +8,11 @@ using namespace speckle::utility;
Constructor
selected: Information about a selected Archicad element
tableID: The ID of the parent table
--------------------------------------------------------------------*/
//Link::Link(const API_Neig& selected) : base{Guid{selected.guid}} {
//More info should be extracted from API_Neig in future (as required)
//} //Link::Link
BIMLink::BIMLink(const API_Neig& selected, const BIMRecordID& tableID) : base{Guid{selected.guid}, tableID} {
//More info should be extracted from API_Neig in future (as required) - extract into link settings, e.g. selection target etc
} //Link::Link
#endif
@@ -0,0 +1,48 @@
#ifndef SPECKLE_DATABASE_BIM_LINK
#define SPECKLE_DATABASE_BIM_LINK
#include "Active/Setting/SettingList.h"
#include "Active/Database/Identity/Link.h"
#include "Speckle/Database/Identity/BIMRecordID.h"
namespace speckle::database {
/*!
Record link class, binding an index to additional information
The essential part of a link is the index pointing to a target record. In some cases, e.g. where the user has selected something in a rendered
scene, the index is to a proxy object that represents another record in a specific context, e.g. a 2D representation of a 3D element rendered
in a cut plance section view. A tool working with the link might be interested in either the proxy or the original - passing a link allows
this distinction to be easily made.
A link may optionally carry any number of settings. In the context of a user selection (for example) there might be settings describing where
the user made the selection (e.g. the hole in a floor slab), allowing a tool working on that selection to be more precise.
*/
class BIMLink : public active::database::Link<BIMRecordID> {
public:
// MARK: - Types
using base = active::database::Link<BIMRecordID>;
// MARK: - Constructors
using base::base;
BIMLink() = default;
#ifdef ARCHICAD
/*!
Constructor
@param selected Information about a selected Archicad element
@param tableID The ID of the parent table
*/
BIMLink(const API_Neig& selected, const BIMRecordID& tableID);
#endif
};
//A list of links to BIM records
using BIMLinkList = std::vector<BIMLink>;
}
#endif //SPECKLE_DATABASE_BIM_LINK
@@ -0,0 +1,24 @@
#ifndef SPECKLE_DATABASE_BIM_ID
#define SPECKLE_DATABASE_BIM_ID
#include "Speckle/Utility/Guid.h"
namespace speckle::database {
#ifdef ARCHICAD
//Common BIM record identifier type (e.g. to BIM elements)
using BIMRecordID = speckle::utility::Guid;
//Common BIM table identifier type (e.g. primary model, views)
using BIMTableID = speckle::utility::Guid;
//Common BIM document identifier type (e.g. BIM drawing identifier, e.g. section, layout etc)
using BIMDocID = speckle::utility::Guid;
//Common BIM database identifier type (e.g. model database, library database, attribute database)
using BIMDBaseID = speckle::utility::Guid;
#endif
//A list of BIM record IDs
using BIMRecordIDList = std::vector<BIMRecordID>;
}
#endif //SPECKLE_DATABASE_BIM_ID
+1 -4
View File
@@ -1,11 +1,8 @@
#ifndef SPECKLE_DATABASE_INDEX
#define SPECKLE_DATABASE_INDEX
#include "Active/Database/Identity/Link.h"
#include "Active/Database/Identity/Index.h"
#include "Speckle/Database/Identity/RecordID.h"
#include "Speckle/Utility/String.h"
#include <any>
namespace speckle::database {
+1 -7
View File
@@ -2,6 +2,7 @@
#define SPECKLE_DATABASE_LINK
#include "Active/Setting/SettingList.h"
#include "Active/Database/Identity/Link.h"
#include "Speckle/Database/Identity/Index.h"
namespace speckle::database {
@@ -29,13 +30,6 @@ namespace speckle::database {
using base::base;
Link() = default;
#ifdef ARCHICAD
/*!
Constructor
@param selected Information about a selected Archicad element
*/
Link(const API_Neig& selected);
#endif
};
}
@@ -0,0 +1,95 @@
#include "Speckle/Database/Storage/ArchicadDBase/ArchicadDBaseCore.h"
#ifdef ARCHICAD
#include "Active/Utility/Defer.h"
#include "Active/Utility/Memory.h"
#include "Active/Utility/String.h"
#include "Speckle/Environment/Addon.h"
#include "Speckle/Environment/Project.h"
#include "Speckle/Event/Type/DocStoreMergeEvent.h"
#include "Speckle/Event/Type/ProjectEvent.h"
#include "Speckle/Utility/Guid.h"
#include "Speckle/Utility/String.h"
#include <ACAPinc.h>
#include <BM.hpp>
using namespace active::event;
using namespace active::setting;
using namespace speckle::database;
using namespace speckle::environment;
using namespace speckle::event;
using namespace speckle::utility;
using enum ArchicadDBaseCore::Status;
namespace {
/*--------------------------------------------------------------------
Convert an Archicad API error code to a ArchicadDBaseCore status
acErrorCode: The API error code
return: An equivalent status code
--------------------------------------------------------------------*/
ArchicadDBaseCore::Status convertArchicadError(long acErrorCode) {
using enum ArchicadDBaseCore::Status;
switch (acErrorCode) {
case NoError:
return nominal;
case APIERR_BADPARS:
return badParameter;
case APIERR_BADID:
return badID;
default:
break;
}
return error;
} //convertArchicadError
///Category for ArchicadElementDBase processing errors
class ArchicadDBaseCategory : public std::error_category {
public:
///Category name
const char* name() const noexcept override {
return "speckle::database::archicad::category";
}
/*!
Get a message for a specified error code
@param errorCode A ArchicadElementDBase processing code
@return The error message for the specified code
*/
std::string message(int errorCode) const override {
//TODO: These error messages are ok for developers - but can we help users more?
switch (static_cast<ArchicadDBaseCore::Status>(errorCode)) {
case nominal:
return "";
case badParameter:
return "An internal function has been incorrectly used";
case badID:
return "Internal data has been requested using an invalid identity";
case error:
return "A non-specific error occurred";
default:
return "An unknown error occurred";
}
}
};
///ArchicadElementDBase processing category error instance
static ArchicadDBaseCategory instance;
}
/*--------------------------------------------------------------------
Make an error code for ArchicadElementDBase processing
return: An STL error code
--------------------------------------------------------------------*/
std::error_code ArchicadDBaseCore::makeError(ArchicadDBaseCore::Status code) {
return std::error_code(static_cast<int>(code), instance);
} //ArchicadDBaseCore::makeError
#endif
@@ -0,0 +1,86 @@
#ifndef SPECKLE_DATABASE_ARCHICAD_DBASE_CORE
#define SPECKLE_DATABASE_ARCHICAD_DBASE_CORE
#ifdef ARCHICAD
#include "Active/File/Path.h"
#include "Active/Setting/SettingList.h"
#include "Active/Database/Storage/DBaseSchema.h"
#include "Active/Utility/NameID.h"
#include "Speckle/Event/Subscriber/DocStoreSubscriber.h"
#include "Speckle/Event/Subscriber/ProjectSubscriber.h"
namespace speckle::database {
using ArchicadDBaseSchema = active::database::DBaseSchema<>;
/*!
Core functionality and definitions for a mechanism to store data in a BIM (3rd-party) document/database
Currently implement for Archicad Add-On Objects
*/
class ArchicadDBaseCore {
public:
// MARK: - Types
///Status of of the ArchicadElementDBase database
enum class Status {
nominal, ///<No errors logged
badParameter, ///<The data supplied to an SDK function call was invalid
badID, ///<The ID for the stored data is invalid
error, ///<An unidentified error occurred
};
// MARK: - Static functions
/*!
Make an error code for ArchicadElementDBase processing
@return An STL error code
*/
static std::error_code makeError(ArchicadDBaseCore::Status code);
// MARK: - Constructors
/*!
Default constructor
@param id The document storage identifier
@param schema The document storage schema
*/
ArchicadDBaseCore(const active::utility::NameID& id, ArchicadDBaseSchema&& schema) : m_id(id), m_schema{schema} {}
ArchicadDBaseCore(const ArchicadDBaseCore&) = default;
/*!
Destructor
*/
virtual ~ArchicadDBaseCore() {}
// MARK: - Function (const)
/*!
Get the database schema
@return The database schema
*/
const ArchicadDBaseSchema& getSchema() const { return m_schema; }
/*!
Get the database id
@return The database id
*/
const active::utility::NameID& getID() const { return m_id; }
// MARK: - Functions (mutating)
protected:
private:
///The database schema
ArchicadDBaseSchema m_schema;
///The database ID (mutable to allow lazy loading when data is accessed)
mutable active::utility::NameID m_id;
};
}
#endif //ARCHICAD
#endif //SPECKLE_DATABASE_ARCHICAD_DBASE_CORE
@@ -0,0 +1,297 @@
#include "Speckle/Database/Storage/ArchicadDBase/Attribute/ArchicadAttributeDBaseEngine.h"
#ifdef ARCHICAD
#include "Active/Utility/Defer.h"
#include "Active/Utility/Memory.h"
#include "Active/Utility/String.h"
#include "Speckle/Record/Attribute/Finish.h"
#include "Speckle/Record/Attribute/Storey.h"
#include "Speckle/Database/Identity/BIMLink.h"
#include "Speckle/Environment/Addon.h"
#include "Speckle/Environment/Project.h"
#include "Speckle/Event/Type/DocStoreMergeEvent.h"
#include "Speckle/Event/Type/ProjectEvent.h"
#include "Speckle/Utility/Guid.h"
#include "Speckle/Utility/String.h"
#include <ACAPinc.h>
#include <ACAPI_Database.h>
#include <BM.hpp>
using namespace active::event;
using namespace active::setting;
using namespace speckle::database;
using namespace speckle::environment;
using namespace speckle::event;
using namespace speckle::record::attribute;
using namespace speckle::utility;
using enum ArchicadDBaseCore::Status;
namespace speckle::database {
#ifdef ARCHICAD
/*!
A class to collect and manage a list of active Archicad storeys
NB: This list has to be rebuilt every time a storey has changed because Archicad reindexes storeys each time
This class also has to release memory used by the API to hold storey data
*/
class ArchicadAttributeDBaseEngine::StoreyCache : public std::vector<API_StoryType> {
public:
/*!
Constructor (NB: This automatically gathers information about the current storeys)
*/
StoreyCache() { rebuild(); }
/*!
Rebuild the current storey list
*/
void rebuild() {
clear();
API_StoryInfo storeyInfo;
active::utility::Memory::erase(storeyInfo);
ACAPI_ProjectSetting_GetStorySettings(&storeyInfo);
auto storeyCount = storeyInfo.lastStory - storeyInfo.firstStory + 1;
for (auto i = 0; i < storeyCount; ++i)
push_back((*storeyInfo.data)[i]);
BMKillHandle(reinterpret_cast<GSHandle*>(&storeyInfo.data));
}
/*!
Find a storey by unique ID
@param id The storey unique ID
@return An iterator pointing to the found storey (end() on failure)
*/
const_iterator find(const Guid& id) const {
auto floorID = static_cast<short>(Guid::toInt(id));
return std::find_if(begin(), end(), [&](auto storey){ return storey.floorId == floorID; });
}
/*!
Find a storey by index
@param index The storey index
@return An iterator pointing to the found storey (end() on failure)
*/
const_iterator find(short index) const {
return std::find_if(begin(), end(), [&](auto storey){ return storey.index == index; });
}
};
#endif
}
namespace {
///Attribute factory
std::unordered_map<active::utility::Guid, std::function<Attribute::Unique(API_Attribute, const active::utility::Guid&)>> attributeFactory;
#ifdef ARCHICAD
/*!
Make a new attribute object
@param attributeData The API attribute representation
@return A new attribute object (nullptr on failure)
*/
Attribute::Unique makeAttribute(const API_Attribute& attributeData) {
auto tableID = active::utility::Guid::fromInt(static_cast<uint64_t>(attributeData.header.typeID));
if (attributeFactory.empty()) {
attributeFactory[active::utility::Guid::fromInt(static_cast<uint64_t>(attributeData.header.typeID))] =
[](API_Attribute attrData, const active::utility::Guid& tableID){ return std::make_unique<Finish>(attrData, tableID); };
}
if (auto iter = attributeFactory.find(tableID); iter != attributeFactory.end())
return iter->second(attributeData, tableID);
return nullptr;
}
/*!
Get the AC API data for a specified attribute
@param ID The attribute ID
@param tableID The parent table ID
@return A new attribute object (nullptr on failure)
*/
std::optional<API_Attribute> getAPIData(const BIMRecordID& ID, std::optional<BIMRecordID> tableID) {
API_Attribute attribute;
active::utility::Memory::erase(attribute);
attribute.header.index = ACAPI_CreateAttributeIndex(static_cast<int32_t>(Guid::toInt(ID)));
attribute.header.typeID = static_cast<API_AttrTypeID>(Guid::toInt(*tableID));
if (ACAPI_Attribute_Get(&attribute) != NoError)
return std::nullopt;
return attribute;
}
#endif
}
/*--------------------------------------------------------------------
Constructor
id: The document storage identifier
--------------------------------------------------------------------*/
ArchicadAttributeDBaseEngine::ArchicadAttributeDBaseEngine(const active::utility::NameID& id, ArchicadDBaseSchema&& schema) :
ArchicadDBaseCore{id, std::move(schema)} {
} //ArchicadAttributeDBaseEngine::ArchicadAttributeDBaseEngine
/*--------------------------------------------------------------------
Destructor
--------------------------------------------------------------------*/
ArchicadAttributeDBaseEngine::~ArchicadAttributeDBaseEngine() {
} //ArchicadAttributeDBaseEngine::~ArchicadAttributeDBaseEngine
/*--------------------------------------------------------------------
Get an object by ID
objID: The object index
tableID: Optional table ID (defaults to the floor plan)
documentID: Optional document ID (when the object is bound to a specific document)
return: The requested object (nullptr on failure)
--------------------------------------------------------------------*/
std::unique_ptr<Attribute> ArchicadAttributeDBaseEngine::getObject(const BIMRecordID& objID, std::optional<BIMRecordID> tableID,
std::optional<BIMRecordID> documentID) const {
if (!tableID)
return nullptr;
if (auto attrData = ::getAPIData(objID, *tableID); attrData)
return makeAttribute(*attrData);
return nullptr;
} //ArchicadAttributeDBaseEngine::getObject
/*--------------------------------------------------------------------
Get an object in a transportable form, e.g. packaged for serialisation
index: The object index
tableID: Optional table ID (defaults to the floor plan)
documentID: Optional document ID (when the object is bound to a specific document)
return: The requested wrapped cargo (nullptr on failure)
--------------------------------------------------------------------*/
active::serialise::Cargo::Unique ArchicadAttributeDBaseEngine::getObjectCargo(const BIMRecordID& ID, std::optional<BIMRecordID> tableID,
std::optional<BIMRecordID> documentID) const {
return nullptr; //TODO: Implement
} //ArchicadAttributeDBaseEngine::getObject
/*--------------------------------------------------------------------
Get all objects
tableID: Optional table ID (defaults to the floor plan)
documentID: Optional document ID (filter for this document only - nullopt = all objects)
return: The requested objects (nullptr on failure)
--------------------------------------------------------------------*/
active::container::Vector<Attribute> ArchicadAttributeDBaseEngine::getObjects(std::optional<BIMRecordID> tableID,
std::optional<BIMRecordID> documentID) const {
return {}; //TODO: Implement
} //ArchicadAttributeDBaseEngine::getObjects
/*--------------------------------------------------------------------
Get all objects
filter: The object filter
tableID: Optional table ID (defaults to the floor plan)
documentID: Optional document ID (filter for this document only - nullopt = all objects)
return: The requested objects (nullptr on failure)
--------------------------------------------------------------------*/
active::container::Vector<Attribute> ArchicadAttributeDBaseEngine::getObjects(const Filter& filter, std::optional<BIMRecordID> tableID,
std::optional<BIMRecordID> documentID) const {
return {}; //TODO: Implement
} //ArchicadAttributeDBaseEngine::getObjects
/*--------------------------------------------------------------------
Write an object to the database
object: The object to write
objID: The object ID
objDocID: The object document-specific ID (unique within a specific document - nullopt if not document-bound)
tableID: Optional table ID (defaults to the floor plan)
documentID: Optional document ID (when the object is bound to a specific document)
--------------------------------------------------------------------*/
void ArchicadAttributeDBaseEngine::write(const Attribute& object, const BIMRecordID& objID, std::optional<BIMRecordID> objDocID,
std::optional<BIMRecordID> tableID, std::optional<BIMRecordID> documentID) const {
//TODO: Implement
} //ArchicadAttributeDBaseEngine::write
/*--------------------------------------------------------------------
Erase an object by index
objID: The object ID
tableID: Optional table ID (defaults to the floor plan)
documentID: Optional document ID (when the object is bound to a specific document)
return: True if the object was successfully erased
--------------------------------------------------------------------*/
void ArchicadAttributeDBaseEngine::erase(const BIMRecordID& ID, std::optional<BIMRecordID> tableID,
std::optional<BIMRecordID> documentID) const {
//TODO: Implement
} //ArchicadAttributeDBaseEngine::erase
/*--------------------------------------------------------------------
Erase all objects
tableID: Optional table ID (defaults to the floor plan)
documentID: Optional document ID (filter for this document only - nullopt = all objects)
--------------------------------------------------------------------*/
void ArchicadAttributeDBaseEngine::erase(std::optional<BIMRecordID> tableID, std::optional<BIMRecordID> documentID) const {
//TODO: Implement
} //ArchicadAttributeDBaseEngine::erase
/*--------------------------------------------------------------------
Get the database outline
return: The database outline
--------------------------------------------------------------------*/
ArchicadAttributeDBaseEngine::Outline ArchicadAttributeDBaseEngine::getOutline() const {
return {}; //TODO: Implement
} //ArchicadAttributeDBaseEngine::getOutline
/*--------------------------------------------------------------------
Get attribute data direct from the AC API. For internal use - avoid direct use
link: A link to the required attribute
return: The AC API attribute data
--------------------------------------------------------------------*/
std::optional<API_Attribute> ArchicadAttributeDBaseEngine::getAPIData(const BIMLink& link) const {
return ::getAPIData(link, link.tableID);
} //ArchicadAttributeDBaseEngine::getAPIData
/*--------------------------------------------------------------------
Get storey data direct from the AC API. For internal use - avoid direct use
link: A link to the required storey
return: The AC API storey data
--------------------------------------------------------------------*/
std::optional<API_StoryType> ArchicadAttributeDBaseEngine::getAPIStorey(const BIMLink& link) const {
if (!m_storeyCache)
m_storeyCache = std::make_unique<StoreyCache>();
if (auto iter = m_storeyCache->find(link); iter != m_storeyCache->end())
return *iter;
return std::nullopt;
} //ArchicadAttributeDBaseEngine::getAPIStorey
/*--------------------------------------------------------------------
Get the ID of a storey from a specified index
index: The storey index
return: The storey ID (nullopt on failure)
--------------------------------------------------------------------*/
std::optional<BIMRecordID> ArchicadAttributeDBaseEngine::getStoreyID(short index) const {
if (!m_storeyCache)
m_storeyCache = std::make_unique<StoreyCache>();
if (auto iter = m_storeyCache->find(index); iter != m_storeyCache->end())
return Guid::fromInt(iter->floorId);
return std::nullopt;
} //ArchicadAttributeDBaseEngine::getStoreyID
#endif
@@ -0,0 +1,148 @@
#ifndef SPECKLE_DATABASE_ARCHICAD_ATTRIBUTE_DBASE_ENGINE
#define SPECKLE_DATABASE_ARCHICAD_ATTRIBUTE_DBASE_ENGINE
#include "Active/Database/Storage/DBaseEngine.h"
#include "Active/Serialise/UnboxedTransport.h"
#include "Speckle/Database/Storage/ArchicadDBase/ArchicadDBaseCore.h"
#include "Speckle/Database/Identity/BIMLink.h"
#include "Speckle/Record/Attribute/Attribute.h"
#include "Speckle/Utility/Guid.h"
#include "Speckle/Utility/String.h"
#include <algorithm>
#include <ranges>
namespace speckle::database {
/*!
A database engine to read/write elements in an Archicad project database (local file or cloud-based)
For attribute indices:
- Each attribute type is considered to be stored in a dedicated table
- For Archicad:
- The table ID is typically the attribute type ID
- The record ID is the attribute index
- Storeys are also treated as an attribute (the API treats them separately, although application to elements is virtually the identical)
*/
class ArchicadAttributeDBaseEngine : public ArchicadDBaseCore,
public active::database::DBaseEngine<record::attribute::Attribute, BIMRecordID, BIMRecordID, BIMRecordID> {
public:
// MARK: - Types
using base = active::database::DBaseEngine<record::attribute::Attribute, BIMRecordID, BIMRecordID, BIMRecordID>;
using Attribute = record::attribute::Attribute;
using Filter = base::Filter;
using Outline = base::Outline;
// MARK: - Constructors
/*!
Constructor
@param id The document storage identifier
*/
ArchicadAttributeDBaseEngine(const active::utility::NameID& id, ArchicadDBaseSchema&& schema);
ArchicadAttributeDBaseEngine(const ArchicadAttributeDBaseEngine&) = delete;
/*!
Destructor
*/
~ArchicadAttributeDBaseEngine();
// MARK: - Functions (const)
/*!
Get an object by ID
@param objID The object ID
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (when the object is bound to a specific document)
@return The requested object (nullptr on failure)
*/
std::unique_ptr<Attribute> getObject(const BIMRecordID& objID, std::optional<BIMRecordID> tableID = std::nullopt, std::optional<BIMRecordID> documentID = std::nullopt) const override;
/*!
Get an object in a transportable form, e.g. packaged for serialisation
@param objID The object ID
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (when the object is bound to a specific document)
@return: The requested wrapped cargo (nullptr on failure)
*/
active::serialise::Cargo::Unique getObjectCargo(const BIMRecordID& objID, std::optional<BIMRecordID> tableID = std::nullopt, std::optional<BIMRecordID> documentID = std::nullopt) const override;
/*!
Get all objects
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (filter for this document only - nullopt = all objects)
@return The requested objects (nullptr on failure)
*/
active::container::Vector<Attribute> getObjects(std::optional<BIMRecordID> tableID = std::nullopt, std::optional<BIMRecordID> documentID = std::nullopt) const override;
/*!
Get a filtered list of objects
@param filter The object filter
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (filter for this document only - nullopt = all objects)
@return The filtered objects (nullptr on failure)
*/
active::container::Vector<Attribute> getObjects(const Filter& filter, std::optional<BIMRecordID> tableID = std::nullopt,
std::optional<BIMRecordID> documentID = std::nullopt) const override;
/*!
Write an object to the database
@param object The object to write
@param objID The object ID
@param objDocID The object document-specific ID (unique within a specific document - nullopt if not document-bound)
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (when the object is bound to a specific document)
*/
void write(const Attribute& object, const BIMRecordID& objID, std::optional<BIMRecordID> objDocID = std::nullopt,
std::optional<BIMRecordID> tableID = std::nullopt, std::optional<BIMRecordID> documentID = std::nullopt) const override;
/*!
Erase an object by index
@param ID The object ID
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (when the object is bound to a specific document)
@throw Exception thrown on SQL error
*/
void erase(const BIMRecordID& ID, std::optional<BIMRecordID> tableID = std::nullopt,
std::optional<BIMRecordID> documentID = std::nullopt) const override;
/*!
Erase all objects
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (when the object is bound to a specific document)
@throw Exception thrown on SQL error
*/
void erase(std::optional<BIMRecordID> tableID = std::nullopt, std::optional<BIMRecordID> documentID = std::nullopt) const override;
/*!
Get the database outline
@return The database outline
*/
Outline getOutline() const override;
#ifdef ARCHICAD
/*!
Get attribute data direct from the AC API. For internal use - avoid direct use
@param link A link to the required attribute
@return The AC API attribute data
*/
std::optional<API_Attribute> getAPIData(const BIMLink& link) const;
/*!
Get storey data direct from the AC API. For internal use - avoid direct use
@param link A link to the required storey
@return The AC API storey data
*/
std::optional<API_StoryType> getAPIStorey(const BIMLink& link) const;
/*!
Get the ID of a storey from a specified index
@param index The storey index
@return The storey ID (nullopt on failure)
*/
std::optional<BIMRecordID> getStoreyID(short index) const;
#endif
private:
void setTable(std::optional<BIMRecordID> tableID = std::nullopt, std::optional<BIMRecordID> documentID = std::nullopt);
class StoreyCache;
///Cache of storeys in the database (saves repeated lookups)
mutable std::unique_ptr<StoreyCache> m_storeyCache;
};
}
#endif //SPECKLE_DATABASE_ARCHICAD_ATTRIBUTE_DBASE_ENGINE
@@ -0,0 +1,257 @@
#include "Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.h"
#ifdef ARCHICAD
#include "Active/Utility/Defer.h"
#include "Active/Utility/Memory.h"
#include "Active/Utility/String.h"
#include "Speckle/Database/Identity/BIMLink.h"
#include "Speckle/Environment/Addon.h"
#include "Speckle/Environment/Project.h"
#include "Speckle/Event/Type/DocStoreMergeEvent.h"
#include "Speckle/Event/Type/ProjectEvent.h"
#include "Speckle/Record/Element/Column.h"
#include "Speckle/Record/Element/ColumnSegment.h"
#include "Speckle/Record/Element/GenericElement.h"
#include "Speckle/Record/Element/Memo.h"
#include "Speckle/Utility/Guid.h"
#include "Speckle/Utility/String.h"
#include <ACAPinc.h>
#include <ACAPI_Database.h>
#include <BM.hpp>
using namespace active::event;
using namespace active::setting;
using namespace speckle::database;
using namespace speckle::environment;
using namespace speckle::event;
using namespace speckle::record::element;
using namespace speckle::utility;
using enum ArchicadDBaseCore::Status;
namespace {
/*!
Get information about a specified Archicad table
@param tableID The ID of the target table
@return The requested table info (nullopt on failure)
*/
std::optional<API_DatabaseInfo> getTableInfo(const BIMRecordID& tableID) {
API_DatabaseInfo dbaseInfo;
dbaseInfo.databaseUnId.elemSetId = tableID;
if (auto err = ACAPI_Window_GetDatabaseInfo(&dbaseInfo); err == NoError)
return dbaseInfo;
return std::nullopt;
} //getTableInfo
/*!
Set the active Archicad table
@param tableID The target table ID
@return True on success
*/
bool setActiveTable(const BIMRecordID& tableID) {
if (!tableID)
return false; //Null guid doens't point to anything
if (auto activeTable = ArchicadElementDBaseEngine::getActiveTable(); activeTable && *activeTable == tableID)
return true;
auto dbaseInfo = getTableInfo(tableID);
if (!dbaseInfo)
return false;
return ACAPI_Database_ChangeCurrentDatabase(&*dbaseInfo) == NoError;
} //setActiveTable
/*!
Make a new element object
@param elementData The API element representation
@param tableID The ID of the parent table (defaults to the active drawing)
@return A new element object (nullptr on failure)
*/
Element::Unique makeElement(const API_Element& elementData, const BIMRecordID& tableID) {
switch (elementData.header.type.typeID) {
case API_ColumnID:
return std::make_unique<Column>(elementData, tableID);
case API_ColumnSegmentID:
return std::make_unique<ColumnSegment>(elementData, tableID);
default:
return std::make_unique<GenericElement>(elementData, tableID);
}
}
}
/*--------------------------------------------------------------------
Get the ID of the active Archicad table
return; The active table ID (nullopt on failure)
--------------------------------------------------------------------*/
std::optional<BIMRecordID> ArchicadElementDBaseEngine::getActiveTable() {
API_WindowInfo dbaseInfo;
active::utility::Memory::erase(dbaseInfo);
if (auto err = ACAPI_Database_GetCurrentDatabase(&dbaseInfo); err == NoError)
return dbaseInfo.databaseUnId.elemSetId;
return std::nullopt;
} //ArchicadElementDBaseEngine::getActiveTable
/*--------------------------------------------------------------------
Get the current user element selection
return: A list of selected element IDs
--------------------------------------------------------------------*/
BIMLinkList ArchicadElementDBaseEngine::getSelection() const {
auto tableID = getActiveTable();
if (!tableID)
return {};
BIMLinkList result;
API_SelectionInfo selectionInfo;
active::utility::Memory::erase(selectionInfo);
GS::Array<API_Neig> selection;
if (auto err = ACAPI_Selection_Get(&selectionInfo, &selection, true); err == NoError) {
for (const auto& item : selection)
result.push_back(BIMLink{item, *tableID});
}
return result;
} //ArchicadElementDBaseEngine::getSelection
/*--------------------------------------------------------------------
Get an object by index
index: The object index
tableID: Optional table ID (defaults to the floor plan)
documentID: Optional document ID (when the object is bound to a specific document)
return: The requested object (nullptr on failure)
--------------------------------------------------------------------*/
std::unique_ptr<Element> ArchicadElementDBaseEngine::getObject(const BIMRecordID& ID, std::optional<BIMRecordID> tableID,
std::optional<BIMRecordID> documentID) const {
//Check for memo table requests
if (tableID == memoTable) {
auto memo = std::make_unique<API_ElementMemo>();
active::utility::Memory::erase(*memo);
//Use memo filtering when requested
uint64_t filter = documentID ? Guid::toInt(*documentID) : APIMemoMask_All;
if (auto err = ACAPI_Element_GetMemo(ID, memo.get(), filter); err != NoError)
ACAPI_DisposeElemMemoHdls(memo.get());
else {
auto result = std::make_unique<Memo>();
result->set(std::move(memo));
return result;
}
}
if (!tableID) {
//Use the active table if none is specified
tableID = getActiveTable();
if (!tableID)
return nullptr;
}
API_Element element;
active::utility::Memory::erase(element);
API_Guid guid{ID.operator API_Guid()};
if (ACAPI_Element_GetElementFromAnywhere(&guid, &element) != NoError)
return nullptr;
return makeElement(element, *tableID);
} //ArchicadElementDBaseEngine::getObject
/*--------------------------------------------------------------------
Get an object in a transportable form, e.g. packaged for serialisation
index: The object index
tableID: Optional table ID (defaults to the floor plan)
documentID: Optional document ID (when the object is bound to a specific document)
return: The requested wrapped cargo (nullptr on failure)
--------------------------------------------------------------------*/
active::serialise::Cargo::Unique ArchicadElementDBaseEngine::getObjectCargo(const BIMRecordID& ID, std::optional<BIMRecordID> tableID,
std::optional<BIMRecordID> documentID) const {
return nullptr; //TODO: Implement
} //ArchicadElementDBaseEngine::getObject
/*--------------------------------------------------------------------
Get all objects
tableID: Optional table ID (defaults to the floor plan)
documentID: Optional document ID (filter for this document only - nullopt = all objects)
return: The requested objects (nullptr on failure)
--------------------------------------------------------------------*/
active::container::Vector<Element> ArchicadElementDBaseEngine::getObjects(std::optional<BIMRecordID> tableID,
std::optional<BIMRecordID> documentID) const {
if (tableID)
setActiveTable(*tableID);
return {}; //TODO: Implement
} //ArchicadElementDBaseEngine::getObjects
/*--------------------------------------------------------------------
Get all objects
filter: The object filter
tableID: Optional table ID (defaults to the floor plan)
documentID: Optional document ID (filter for this document only - nullopt = all objects)
return: The requested objects (nullptr on failure)
--------------------------------------------------------------------*/
active::container::Vector<Element> ArchicadElementDBaseEngine::getObjects(const Filter& filter, std::optional<BIMRecordID> tableID,
std::optional<BIMRecordID> documentID) const {
return {};
} //ArchicadElementDBaseEngine::getObjects
/*--------------------------------------------------------------------
Write an object to the database
object: The object to write
objID: The object ID
objDocID: The object document-specific ID (unique within a specific document - nullopt if not document-bound)
tableID: Optional table ID (defaults to the floor plan)
documentID: Optional document ID (when the object is bound to a specific document)
--------------------------------------------------------------------*/
void ArchicadElementDBaseEngine::write(const Element& object, const BIMRecordID& objID, std::optional<BIMRecordID> objDocID,
std::optional<BIMRecordID> tableID, std::optional<BIMRecordID> documentID) const {
} //ArchicadElementDBaseEngine::write
/*--------------------------------------------------------------------
Erase an object by index
objID: The object ID
tableID: Optional table ID (defaults to the floor plan)
documentID: Optional document ID (when the object is bound to a specific document)
return: True if the object was successfully erased
--------------------------------------------------------------------*/
void ArchicadElementDBaseEngine::erase(const BIMRecordID& ID, std::optional<BIMRecordID> tableID,
std::optional<BIMRecordID> documentID) const {
} //ArchicadElementDBaseEngine::erase
/*--------------------------------------------------------------------
Erase all objects
tableID: Optional table ID (defaults to the floor plan)
documentID: Optional document ID (filter for this document only - nullopt = all objects)
--------------------------------------------------------------------*/
void ArchicadElementDBaseEngine::erase(std::optional<BIMRecordID> tableID, std::optional<BIMRecordID> documentID) const {
} //ArchicadElementDBaseEngine::erase
/*--------------------------------------------------------------------
Get the database outline
return: The database outline
--------------------------------------------------------------------*/
ArchicadElementDBaseEngine::Outline ArchicadElementDBaseEngine::getOutline() const {
return {};
} //ArchicadElementDBaseEngine::getOutline
#endif
@@ -0,0 +1,130 @@
#ifndef SPECKLE_DATABASE_ARCHICAD_ELEMENT_DBASE_ENGINE
#define SPECKLE_DATABASE_ARCHICAD_ELEMENT_DBASE_ENGINE
#include "Active/Database/Storage/DBaseEngine.h"
#include "Active/Serialise/UnboxedTransport.h"
#include "Speckle/Database/Storage/ArchicadDBase/ArchicadDBaseCore.h"
#include "Speckle/Database/Identity/BIMLink.h"
#include "Speckle/Record/Element/Element.h"
#include "Speckle/Utility/Guid.h"
#include "Speckle/Utility/String.h"
#include <algorithm>
#include <ranges>
namespace speckle::database {
/*!
A database engine to read/write elements in an Archicad project database (local file or cloud-based)
*/
class ArchicadElementDBaseEngine : public ArchicadDBaseCore,
public active::database::DBaseEngine<record::element::Element, BIMRecordID, BIMRecordID, BIMRecordID> {
public:
// MARK: - Types
using base = active::database::DBaseEngine<record::element::Element, BIMRecordID, BIMRecordID, BIMRecordID>;
using Element = record::element::Element;
using Filter = base::Filter;
using Outline = base::Outline;
// MARK: - Constants
///The memo table ID (supplementary element data)
static const inline utility::Guid memoTable{utility::String{"fdff96d2-8c34-4f8b-8a76-a96a2b242758"}};
// MARK: - Static functions
/*!
Get the ID of the active Archicad table
@return The active table ID (nullopt on failure)
*/
static std::optional<BIMRecordID> getActiveTable();
// MARK: - Constructors
/*!
Constructor
@param id The document storage identifier
*/
ArchicadElementDBaseEngine(const active::utility::NameID& id, ArchicadDBaseSchema&& schema) : ArchicadDBaseCore{id, std::move(schema)} {}
ArchicadElementDBaseEngine(const ArchicadElementDBaseEngine&) = delete;
// MARK: - Functions (const)
/*!
Get the current user element selection
@return A list of selected element IDs
*/
BIMLinkList getSelection() const;
/*!
Get an object by index
@param objID The object ID
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (when the object is bound to a specific document)
@return The requested object (nullptr on failure)
*/
std::unique_ptr<Element> getObject(const BIMRecordID& objID, std::optional<BIMRecordID> tableID = std::nullopt, std::optional<BIMRecordID> documentID = std::nullopt) const override;
/*!
Get an object in a transportable form, e.g. packaged for serialisation
@param objID The object ID
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (when the object is bound to a specific document)
@return: The requested wrapped cargo (nullptr on failure)
*/
active::serialise::Cargo::Unique getObjectCargo(const BIMRecordID& objID, std::optional<BIMRecordID> tableID = std::nullopt, std::optional<BIMRecordID> documentID = std::nullopt) const override;
/*!
Get all objects
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (filter for this document only - nullopt = all objects)
@return The requested objects (nullptr on failure)
*/
active::container::Vector<Element> getObjects(std::optional<BIMRecordID> tableID = std::nullopt, std::optional<BIMRecordID> documentID = std::nullopt) const override;
/*!
Get a filtered list of objects
@param filter The object filter
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (filter for this document only - nullopt = all objects)
@return The filtered objects (nullptr on failure)
*/
active::container::Vector<Element> getObjects(const Filter& filter, std::optional<BIMRecordID> tableID = std::nullopt,
std::optional<BIMRecordID> documentID = std::nullopt) const override;
/*!
Write an object to the database
@param object The object to write
@param objID The object ID
@param objDocID The object document-specific ID (unique within a specific document - nullopt if not document-bound)
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (when the object is bound to a specific document)
*/
void write(const Element& object, const BIMRecordID& objID, std::optional<BIMRecordID> objDocID = std::nullopt,
std::optional<BIMRecordID> tableID = std::nullopt, std::optional<BIMRecordID> documentID = std::nullopt) const override;
/*!
Erase an object by index
@param ID The object ID
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (when the object is bound to a specific document)
@throw Exception thrown on SQL error
*/
void erase(const BIMRecordID& ID, std::optional<BIMRecordID> tableID = std::nullopt,
std::optional<BIMRecordID> documentID = std::nullopt) const override;
/*!
Erase all objects
@param tableID Optional table ID (defaults to the floor plan)
@param documentID Optional document ID (when the object is bound to a specific document)
@throw Exception thrown on SQL error
*/
void erase(std::optional<BIMRecordID> tableID = std::nullopt, std::optional<BIMRecordID> documentID = std::nullopt) const override;
/*!
Get the database outline
@return The database outline
*/
Outline getOutline() const override;
private:
void setTable(std::optional<BIMRecordID> tableID = std::nullopt, std::optional<BIMRecordID> documentID = std::nullopt);
};
}
#endif //SPECKLE_DATABASE_ARCHICAD_ELEMENT_DBASE_ENGINE
@@ -105,7 +105,7 @@ namespace {
public:
///Category name
const char* name() const noexcept override {
return "active::database::sqlite::category";
return "speckle::database::docStore::category";
}
/*!
Get a message for a specified error code
@@ -122,6 +122,11 @@ namespace speckle::database {
@return The database outline
*/
Outline getOutline() const override;
/*!
Get the engine unique ID
@return The unique ID
*/
RecordID getUniqueID() const { return getCache()->getID(); }
protected:
/*!
@@ -1,5 +1,7 @@
#include "Speckle/Environment/Project.h"
#include "Speckle/Database/BIMAttributeDatabase.h"
#include "Speckle/Database/BIMElementDatabase.h"
#include "Speckle/Environment/Addon.h"
#include "Speckle/SpeckleResource.h"
@@ -7,6 +9,7 @@
#include <ACAPinc.h>
#endif
using namespace speckle::database;
using namespace speckle::environment;
using namespace speckle::utility;
@@ -20,6 +23,8 @@ namespace {
identity: Optional name/ID for the subscriber
--------------------------------------------------------------------*/
Project::Project() {
m_element = std::make_unique<BIMElementDatabase>();
m_attribute = std::make_unique<BIMAttributeDatabase>();
} //Project::Project
+21
View File
@@ -4,6 +4,11 @@
#include "Active/File/Path.h"
#include "Speckle/Utility/String.h"
namespace speckle::database {
class BIMAttributeDatabase;
class BIMElementDatabase;
}
namespace speckle::environment {
class Addon;
@@ -50,6 +55,16 @@ namespace speckle::environment {
@return Project information
*/
Info getInfo() const;
/*!
Get the account database
@return The account database
*/
const database::BIMElementDatabase* getElementDatabase() const { return m_element.get(); }
/*!
Get the account database
@return The account database
*/
const database::BIMAttributeDatabase* getAttributeDatabase() const { return m_attribute.get(); }
// MARK: - Functions (mutating)
@@ -62,6 +77,12 @@ namespace speckle::environment {
NB: Only the Addon class can create projects. Clients can get the active project from the running add-on.
*/
Project();
private:
///The BIM element database
std::unique_ptr<database::BIMElementDatabase> m_element;
///The BIM attribute database
std::unique_ptr<database::BIMAttributeDatabase> m_attribute;
};
}
@@ -1,7 +1,8 @@
#include "Speckle/Event/Subscriber/SelectionSubscriber.h"
#include "Speckle/Environment/Addon.h"
#include "Speckle/Database/Identity/Link.h"
#include "Speckle/Database/Identity/BIMLink.h"
#include "Speckle/Database/Storage/ArchicadDBase/Element/ArchicadElementDBaseEngine.h"
#include "Speckle/Event/Type/SelectionEvent.h"
#ifdef ARCHICAD
@@ -23,8 +24,10 @@ namespace {
*/
GSErrCode __ACENV_CALL selectionCallback(const API_Neig* params) {
if (addon() != nullptr) {
auto selection = (params == nullptr) ? Link{} : Link{*params};
addon()->publishExternal(SelectionEvent{selection});
if (auto tableID = ArchicadElementDBaseEngine::getActiveTable(); tableID) {
auto selection = (params == nullptr) ? BIMLink{} : BIMLink{*params, *tableID};
addon()->publishExternal(SelectionEvent{selection});
}
}
return NoError;
}
@@ -32,9 +35,6 @@ namespace {
}
//True if a selection change subscriber has already started (only one is required - there are no variants)
bool speckle::event::SelectionSubscriber::m_isStarted = false;
/*--------------------------------------------------------------------
Get the event subscription list
@@ -66,12 +66,19 @@ bool SelectionSubscriber::receive(const Event& event) {
return: True if the participant is able to continue
--------------------------------------------------------------------*/
bool SelectionSubscriber::start() {
if (m_isStarted)
return true;
m_isStarted = true;
#ifdef ARCHICAD
return (ACAPI_Notification_CatchSelectionChange(selectionCallback) == NoError);
#else
return false;
#endif
} //SelectionSubscriber::start
/*--------------------------------------------------------------------
Stop participation (release resources etc)
--------------------------------------------------------------------*/
void SelectionSubscriber::stop() {
#ifdef ARCHICAD
ACAPI_Notification_CatchSelectionChange(nullptr);
#endif
}
@@ -35,7 +35,7 @@ namespace speckle::event {
Get the event subscription list
@return The subscription list (an empty list will put the subscriber into a suspended state)
*/
virtual Subscription subscription() const override;
Subscription subscription() const override;
// MARK: - Functions (mutating)
@@ -44,24 +44,25 @@ namespace speckle::event {
@param event The incoming event
@return True if the event should be closed
*/
virtual bool receive(const active::event::Event& event) override;
protected:
bool receive(const active::event::Event& event) override;
/*!
Start the participant operation
@return True if the participant is able to continue
*/
virtual bool start() override;
/*!
Handle the menu selection
@param event The menu event
Stop participation (release resources etc)
*/
void stop() override;
protected:
/*!
Handle a selection change
@param event The selection event
@return True if the event should be closed
*/
virtual bool handle(const SelectionEvent& event) = 0;
private:
///True if a selection change subscriber has already started (only one is required - there are no variants)
static bool m_isStarted;
};
}
@@ -5,7 +5,7 @@
#include "Active/Utility/Guid.h"
#include "Active/Utility/String.h"
#include "Speckle/Database/Identity/Link.h"
#include "Speckle/Database/Identity/BIMLink.h"
namespace speckle::event {
@@ -24,7 +24,7 @@ namespace speckle::event {
Constructor
@param selected A link to a selected element (nullopt if the selection is empty)
*/
SelectionEvent(speckle::database::Link::Option selected) : m_selectedLink{selected} {}
SelectionEvent(speckle::database::BIMLink::Option selected) : Event{ ID }, m_selectedLink{selected} {}
/*!
Copy constructor
@param source The object to copy
@@ -46,10 +46,10 @@ namespace speckle::event {
Get a link to the last selected element
@return A link to the last selected element (nullopt if the event selection is empty)
*/
speckle::database::Link::Option getLastSelected() const { return m_selectedLink; }
speckle::database::BIMLink::Option getLastSelected() const { return m_selectedLink; }
private:
speckle::database::Link::Option m_selectedLink;
speckle::database::BIMLink::Option m_selectedLink;
};
}
@@ -38,7 +38,7 @@ namespace speckle::interfac::browser {
*/
template<class Derived>
explicit JSObject(const speckle::utility::String& name, const std::initializer_list<Derived>& items) : base{items}, m_name{name} {}
virtual ~JSObject() {}
// MARK: - Functions (const)
/*!
@@ -48,14 +48,14 @@ namespace speckle::interfac::browser {
@param object The object to install
@return True if the object was successfully installed
*/
bool install(std::shared_ptr<JSObject<FunctionBinding>> object);
std::shared_ptr<JSObject<FunctionBinding>> install(std::shared_ptr<JSObject<FunctionBinding>> object);
/*!
Install a JS function object
@return True if the object was successfully installed
@tparam T The type of object to install
*/
template<typename T> requires std::is_base_of_v<JSObject<FunctionBinding>, T>
bool install() { return install(std::make_shared<T>()); }
std::shared_ptr<JSObject<FunctionBinding>> install() { return install(std::make_shared<T>()); }
protected:
#ifdef ARCHICAD
@@ -101,12 +101,12 @@ namespace speckle::interfac::browser {
return: True if the object was successfully installed
--------------------------------------------------------------------*/
template<typename FunctionBinding>
bool JSPortal<FunctionBinding>::install(std::shared_ptr<JSObject<FunctionBinding>> object) {
std::shared_ptr<JSObject<FunctionBinding>> JSPortal<FunctionBinding>::install(std::shared_ptr<JSObject<FunctionBinding>> object) {
try {
#ifdef ARCHICAD
auto engine = getJSEngine();
if (!engine)
return false;
return nullptr;
//Define the JS object
JS::Object* acObject = new JS::Object(object->getName());
//Add all the functions supported by this object
@@ -125,13 +125,13 @@ namespace speckle::interfac::browser {
if (engine->RegisterAsynchJSObject(acObject)) {
base::push_back(object);
object->setPortal(*this);
return true;
return object;
}
#endif
} catch(...) {
///TODO: Need to discuss the best course of action to notify of a failure
}
return false;
return nullptr;
} //JSPortal<FunctionBinding>::install
}
@@ -0,0 +1,88 @@
#include "Speckle/Primitive/Mesh/Mesh.h"
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include "Active/Serialise/Package/Wrapper/PackageWrap.h"
#include "Active/Serialise/Package/Wrapper/ContainerWrap.h"
#include "Active/Serialise/Inventory/Identity.h"
#include "Speckle/Serialise/Collection/FinishProxy.h"
#include <array>
using namespace active::serialise;
using namespace speckle::primitive;
using namespace speckle::serialise;
namespace {
///Serialisation fields
enum FieldIndex {
vertexID,
faceID,
colorID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"vertices"},
Identity{"faces"},
Identity{"colors"},
};
}
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool Mesh::fillInventory(Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ fieldID[vertexID], vertexID, element },
{ fieldID[faceID], faceID, element },
{ fieldID[colorID], colorID, element },
},
}.withType(&typeid(Mesh)));
return base::fillInventory(inventory);
} //Mesh::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique Mesh::getCargo(const Inventory::Item& item) const {
if (item.ownerType != &typeid(Mesh))
return base::getCargo(item);
using namespace active::serialise;
switch (item.index) {
case vertexID:
return std::make_unique<ContainerWrap<std::vector<double>>>(m_vertices);
case faceID:
return std::make_unique<ContainerWrap<std::vector<int>>>(m_faces);
case colorID:
return std::make_unique<ContainerWrap<std::vector<int>>>(m_colors);
default:
return nullptr; //Requested an unknown index
}
} //Mesh::getCargo
/*--------------------------------------------------------------------
Use a manager in (de)serialisation processes
management: The management to use
--------------------------------------------------------------------*/
void Mesh::useManagement(Management* management) const {
//NB: This object only exists to populate the finish collection - it doesn't carry any serialisable content
if (management != nullptr) {
if (auto collector = management->get<FinishCollector>(); collector != nullptr)
collector->addMaterialProxy(m_material, getBIMID());
}
} //Mesh::useManagement
+80
View File
@@ -0,0 +1,80 @@
#ifndef SPECKLE_PRIMITIVE_MESH
#define SPECKLE_PRIMITIVE_MESH
#include "Speckle/Database/Content/BIMRecord.h"
#include "Speckle/Utility/String.h"
#ifdef ARCHICAD
#include "ModelMaterial.hpp"
#endif
namespace speckle::primitive {
/*!
Class for a 3D mesh
*/
class Mesh : public speckle::database::BIMRecord {
public:
// MARK: - Types
using base = speckle::database::BIMRecord;
// MARK: - Constructors
/*!
Default constructor
@param unit The mesh unit type
*/
Mesh(active::measure::LengthType unit = active::measure::LengthType::metre) : base{utility::Guid{true}, utility::Guid{}, unit} {}
/*!
Constructor
@param vertices The mesh vertices
@param faces The mesh faces (the number of indices in the face followed by the vertex indices)
@param colors The mesh face colours
@param unit The mesh unit type
*/
Mesh(std::vector<double>&& vertices, std::vector<int>&& faces, std::vector<int>&& colors, const ModelerAPI::Material& material,
active::measure::LengthType unit = active::measure::LengthType::metre) :
base{utility::Guid{true}, utility::Guid{}, unit}, m_vertices{std::move(vertices)}, m_faces{std::move(faces)}, m_colors{std::move(colors)}, m_material{material} {}
// MARK: - Functions (const)
/*!
Get the speckle type identifier
@return The speckle type (relevant objects should override as required)
*/
speckle::utility::String getSpeckleType() const override { return "Objects.Geometry.Mesh"; }
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
active::serialise::Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
/*!
Use a manager in (de)serialisation processes
@param management The management to use
*/
void useManagement(active::serialise::Management* management) const override;
private:
std::vector<double> m_vertices;
std::vector<int> m_faces;
std::vector<int> m_colors;
#ifdef ARCHICAD
ModelerAPI::Material m_material;
#endif
};
}
#endif //SPECKLE_PRIMITIVE_MESH
@@ -0,0 +1,105 @@
#include "Speckle/Record/Attribute/Attribute.h"
#include "Speckle/Database/BIMAttributeDatabase.h"
#include "Speckle/Environment/Addon.h"
#include "Speckle/Environment/Project.h"
#include "Speckle/Serialise/Types/Str256.h"
using namespace active::serialise;
using namespace speckle::database;
using namespace speckle::environment;
using namespace speckle::record::attribute;
using namespace speckle::utility;
#include <array>
#include <memory>
namespace {
///Serialisation fields
enum FieldIndex {
nameID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"name"},
};
}
/*--------------------------------------------------------------------
Default constructor
--------------------------------------------------------------------*/
Attribute::Attribute() {
} //Attribute::Attribute
/*--------------------------------------------------------------------
Get the attribute name
return: The attribute name
--------------------------------------------------------------------*/
speckle::utility::String Attribute::getName() const {
#ifdef ARCHICAD
return getHead().name;
#endif
} //Attribute::getName
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool Attribute::fillInventory(Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ fieldID[nameID], nameID, element },
},
}.withType(&typeid(Attribute)));
return base::fillInventory(inventory);
} //Attribute::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique Attribute::getCargo(const Inventory::Item& item) const {
if (item.ownerType != &typeid(Attribute))
return base::getCargo(item);
using namespace active::serialise;
switch (item.index) {
case nameID:
#ifdef ARCHICAD
return std::make_unique<ValueWrap<Str256>>(reinterpret_cast<const Str256&>(getHead().name));
#endif
default:
return nullptr; //Requested an unknown index
}
} //Attribute::getCargo
#ifdef ARCHICAD
/*--------------------------------------------------------------------
Get the attribute data from the host BIM application
return: The attribute data (for internal use to populate derived classes)
--------------------------------------------------------------------*/
API_Attribute Attribute::getData() const {
if (auto project = addon()->getActiveProject().lock(); project) {
if (auto attr = project->getAttributeDatabase()->getAPIData(getBIMLink()); attr)
return *attr;
}
API_Attribute attr;
active::utility::Memory::erase(attr);
return attr;
} //Attribute::getData
#endif
@@ -0,0 +1,120 @@
#ifndef SPECKLE_RECORD_ATTRIBUTE
#define SPECKLE_RECORD_ATTRIBUTE
#include "Speckle/Database/Content/BIMRecord.h"
#include "Speckle/Utility/String.h"
namespace speckle::record::attribute {
/*!
Base BIM attribute class
*/
class Attribute : public speckle::database::BIMRecord {
public:
// MARK: - Types
using base = speckle::database::BIMRecord;
///Unique pointer
using Unique = std::unique_ptr<Attribute>;
///Shared pointer
using Shared = std::shared_ptr<Attribute>;
///Optional
using Option = std::optional<Attribute>;
// MARK: - Constants
#ifdef ARCHICAD
///Archicad type identifier for a storey attribute table
static constexpr int32_t storeyTableID = 0x200;
/*!
Get an attribute type ID from a table ID
@param tableID The table ID
@return The attribute type ID (NB: not strictly API_AttrTypeID - can be storeyTypeID)
*/
static API_AttrTypeID getTypeID(const active::utility::Guid& tableID) {
return static_cast<API_AttrTypeID>(active::utility::Guid::toInt(tableID));
}
/*!
Get an Archicad attribute index from a record ID
@param recordID The record ID
@return An attribute index
*/
static API_AttributeIndex getIndex(const active::utility::Guid& recordID) {
return ACAPI_CreateAttributeIndex(static_cast<int32_t>(active::utility::Guid::toInt(recordID)));
}
#endif
// MARK: - Constructors
using base::base;
/*!
Default constructor
*/
Attribute();
/*!
Constructor
@param ID The attribute ID
@param tableID The attribute table ID (attribute type)
*/
Attribute(const database::BIMRecordID& ID, const speckle::utility::Guid& tableID) : base{ID, tableID} {}
// MARK: - Functions (const)
/*!
Get the speckle type identifier
@return The speckle type (relevant objects should override as required)
*/
speckle::utility::String getSpeckleType() const override { return "speckle::record::attribute::Attribute"; }
/*!
Get the attribute name
@return The attribute name
*/
speckle::utility::String getName() const;
#ifdef ARCHICAD
/*!
Get the (immutable) API attribute header data
@return The attribute header data (only use this data for low-level operations - for normal code, call getters/setters)
*/
virtual const API_Attr_Head& getHead() const = 0;
#endif
// MARK: - Functions (mutating)
#ifdef ARCHICAD
/*!
Get the (mutable) API attribute header data
@return The attribute header data (only use this data for low-level operations - for normal code, call getters/setters)
*/
virtual API_Attr_Head& getHead() = 0;
#endif
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
protected:
#ifdef ARCHICAD
/*!
Get the attribute data from the host BIM application
@return The attribute data (for internal use to populate derived classes)
*/
API_Attribute getData() const;
#endif
};
}
#endif //SPECKLE_RECORD_ATTRIBUTE
@@ -0,0 +1,269 @@
#include "Speckle/Record/Attribute/Finish.h"
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include "Active/Serialise/Management/Management.h"
#include "Active/Serialise/CargoHold.h"
#include "Active/Utility/BufferOut.h"
#include "Speckle/Serialise/Collection/FinishCollector.h"
#include "Speckle/Serialise/Types/ArchicadRGB.h"
#include "Speckle/Utility/Guid.h"
#ifdef ARCHICAD
#include <ACAPinc.h>
#include <ModelMaterial.hpp>
#endif
using namespace active::serialise;
using namespace speckle::database;
using namespace speckle::record::attribute;
using namespace speckle::serialise;
using namespace speckle::utility;
#include <array>
#include <memory>
namespace speckle::record::attribute {
///Internal representation of a rendered finish on a 3D body, i.e. the surface colour/texture etc.
class Finish::Data {
public:
#ifdef ARCHICAD
/*!
Constructor from Archicad surface material
@param attr An Archicad attribute
*/
Data(const API_Attribute& attr) : root{attr.material} {
opacity = 1.0 - (static_cast<double>(attr.material.transpPc) / 100.0);
roughness = 1.0 - (static_cast<double>(attr.material.shine) / 10000.0);
}
///Archicad representation of a surface material
API_MaterialType root;
#endif
//Opacity (0.0 -> 1.0)
double opacity = 1.0;
//Roughness (0.0 -> 1.0)
double roughness = 0.0;
//Metalness (0.0 -> 1.0)
double metalness = 0.0;
};
}
namespace {
///Serialisation fields
enum FieldIndex {
diffuseID,
opacityID,
emissiveID,
metalnessID,
roughnessID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"diffuse"},
Identity{"opacity"},
Identity{"emissive"},
Identity{"metalness"},
Identity{"roughness"},
};
#ifdef ARCHICAD
/*!
Copy a ModelerAPI colour to an AC RGB colour
@param modelColour The modeler API colour
@param colour The attribute API colour
*/
void copyModelerColor(const ModelerAPI::Color& modelColour, API_RGBColor& colour) {
colour.f_red = modelColour.red;
colour.f_green = modelColour.green;
colour.f_blue = modelColour.blue;
}
#endif
}
/*--------------------------------------------------------------------
Default constructor
--------------------------------------------------------------------*/
Finish::Finish() {
} //Finish::Finish
/*--------------------------------------------------------------------
Constructor
ID: The attribute ID
--------------------------------------------------------------------*/
Finish::Finish(const database::BIMRecordID& ID) : base{ID, Finish::table} {
} //Finish::Finish
#ifdef ARCHICAD
/*--------------------------------------------------------------------
Constructor
attrData: Archicad attribute data
tableID: The ID of the parent table
--------------------------------------------------------------------*/
Finish::Finish(const API_Attribute& attrData, const BIMRecordID& tableID) : base{attrData.header.guid, Finish::table} {
m_data = std::make_unique<Data>(attrData);
setUnit(std::nullopt); //Finishes have no unit
}
/*--------------------------------------------------------------------
Constructor
material: A ModelerAPI material definition
--------------------------------------------------------------------*/
Finish::Finish(const ModelerAPI::Material& material) : base{Guid{Guid::fromInt(material.GenerateHashValue())}, Finish::table} {
API_Attribute attr;
active::utility::Memory::erase(attr);
String{material.GetName()}.writeUTF8(active::utility::BufferOut{attr.header.name});
attr.header.guid = getBIMID();
attr.material.mtype = static_cast<API_MaterTypeID>(material.GetType());
attr.material.ambientPc = static_cast<short>(material.GetAmbientReflection() * 100);
attr.material.diffusePc = static_cast<short>(material.GetDiffuseReflection() * 100);
attr.material.specularPc = static_cast<short>(material.GetSpecularReflection() * 100);
attr.material.transpPc = static_cast<short>(material.GetTransparency() * 100);
attr.material.shine = static_cast<short>(material.GetShining() * 100);
attr.material.transpAtt = static_cast<short>(material.GetTransparencyAttenuation() * 400);
attr.material.emissionAtt = static_cast<short>(material.GetEmissionAttenuation() * 65535);
copyModelerColor(material.GetSurfaceColor(), attr.material.surfaceRGB);
copyModelerColor(material.GetSpecularColor(), attr.material.specularRGB);
copyModelerColor(material.GetEmissionColor(), attr.material.emissionRGB);
m_data = std::make_unique<Data>(attr);
setUnit(std::nullopt); //Finishes have no unit
} //Finish::Finish
#endif
/*--------------------------------------------------------------------
Copy constructor
source: The object to copy
--------------------------------------------------------------------*/
Finish::Finish(const Finish& source) : base{source} {
m_data = source.m_data ? std::make_unique<Data>(*source.m_data) : nullptr;
} //Finish::Finish
/*--------------------------------------------------------------------
Destructor
--------------------------------------------------------------------*/
Finish::~Finish() {}
#ifdef ARCHICAD
/*--------------------------------------------------------------------
Get the (immutable) API attribute header data
return: The attribute header data (only use this data for low-level operations - for normal code, call getters/setters)
--------------------------------------------------------------------*/
const API_Attr_Head& Finish::getHead() const {
confirmData();
return m_data->root.head;
} //Finish::getHead
/*--------------------------------------------------------------------
Get the (mutable) API attribute header data
return: The attribute header data (only use this data for low-level operations - for normal code, call getters/setters)
--------------------------------------------------------------------*/
API_Attr_Head& Finish::getHead() {
confirmData();
return m_data->root.head;
} //Finish::getHead
#endif
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool Finish::fillInventory(Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ fieldID[diffuseID], diffuseID, element },
{ fieldID[opacityID], opacityID, element },
{ fieldID[emissiveID], emissiveID, element },
{ fieldID[metalnessID], metalnessID, element },
{ fieldID[roughnessID], roughnessID, element },
},
}.withType(&typeid(Finish)));
return base::fillInventory(inventory);
} //Finish::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique Finish::getCargo(const Inventory::Item& item) const {
if (item.ownerType != &typeid(Finish))
return base::getCargo(item);
confirmData();
using namespace active::serialise;
switch (item.index) {
case diffuseID:
#ifdef ARCHICAD
return std::make_unique<ValueWrap<API_RGBColor>>(m_data->root.surfaceRGB);
#endif
case opacityID:
return std::make_unique<DoubleWrap>(m_data->opacity);
case emissiveID:
#ifdef ARCHICAD
return std::make_unique<ValueWrap<API_RGBColor>>(m_data->root.emissionRGB);
#endif
case metalnessID:
return std::make_unique<DoubleWrap>(m_data->metalness);
case roughnessID:
return std::make_unique<DoubleWrap>(m_data->roughness);
default:
return nullptr; //Requested an unknown index
}
} //Finish::getCargo
/*--------------------------------------------------------------------
Set to the default package content
--------------------------------------------------------------------*/
void Finish::setDefault() {
m_data->root = {};
m_data->opacity = 0.0;
m_data->roughness = 1.0;
m_data->metalness = 0.0;
} //Finish::setDefault
/*--------------------------------------------------------------------
Validate the cargo data
return: True if the data has been validated
--------------------------------------------------------------------*/
bool Finish::validate() {
m_data->root.transpPc = static_cast<short>(100 * (1.0 - m_data->opacity));
m_data->root.shine = static_cast<short>(10000 * (1.0 - m_data->roughness));
//NB: Archicad has no metalness value - currently discarded
return true;
} //Finish::validate
/*--------------------------------------------------------------------
Confirm the internal data, either loading from the BIM application or setting a default
--------------------------------------------------------------------*/
void Finish::confirmData() const {
if (m_data)
return;
m_data = std::make_unique<Data>(getData());
} //Finish::confirmData
@@ -0,0 +1,144 @@
#ifndef SPECKLE_RECORD_ATTRIBUTE_FINISH
#define SPECKLE_RECORD_ATTRIBUTE_FINISH
#include "Speckle/Record/Attribute/Attribute.h"
#ifdef ARCHICAD
namespace ModelerAPI {
class Material;
}
#endif
namespace speckle::record::attribute {
/*!
Class to represent the rendered finish on a 3D body, i.e. the surface colour/texture etc.
In Archicad this attribute is represented by `API_MaterialType`
*/
class Finish : public Attribute {
public:
// MARK: - Types
using base = Attribute;
///Unique pointer
using Unique = std::unique_ptr<Finish>;
///Shared pointer
using Shared = std::shared_ptr<Finish>;
///Optional
using Option = std::optional<Finish>;
// MARK: - Constants
#ifdef ARCHICAD
///The finishes table identifier
static constexpr active::utility::Guid table{active::utility::Guid::fromInt(API_MaterialID)};
#endif
// MARK: - Constructors
using base::base;
/*!
Default constructor
*/
Finish();
/*!
Constructor
@param ID The attribute ID
*/
Finish(const database::BIMRecordID& ID);
#ifdef ARCHICAD
/*!
Constructor
@param attrData Archicad attribute data
@param tableID The ID of the parent table
*/
Finish(const API_Attribute& attrData, const database::BIMRecordID& tableID);
/*!
Constructor
@param material A ModelerAPI material definition
*/
Finish(const ModelerAPI::Material& material);
#endif
/*!
Copy constructor
@param source The object to copy
*/
Finish(const Finish& source);
/*!
Destructor
*/
~Finish();
/*!
Object cloning
@return A clone of this object
*/
Finish* clonePtr() const override { return new Finish{*this}; }
// MARK: - Functions (const)
/*!
Get the speckle type identifier
@return The speckle type (relevant objects should override as required)
*/
speckle::utility::String getSpeckleType() const override { return "Objects.Other.RenderMaterial"; }
#ifdef ARCHICAD
/*!
Get the (immutable) API attribute header data
@return The attribute header data (only use this data for low-level operations - for normal code, call getters/setters)
*/
const API_Attr_Head& getHead() const override;
#endif
// MARK: - Functions (mutating)
#ifdef ARCHICAD
/*!
Get the (mutable) API attribute header data
@return The attribute header data (only use this data for low-level operations - for normal code, call getters/setters)
*/
API_Attr_Head& getHead() override;
#endif
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
/*!
Set to the default package content
*/
void setDefault() override;
/*!
Validate the cargo data
@return True if the data has been validated
*/
bool validate() override;
private:
/*!
Confirm the internal data, either loading from the BIM application or setting a default
*/
void confirmData() const;
class Data;
///The attribute data - mutable to support lazy loading
mutable std::unique_ptr<Data> m_data;
};
}
#endif //SPECKLE_RECORD_ATTRIBUTE_FINISH
@@ -0,0 +1,221 @@
#include "Speckle/Record/Attribute/Storey.h"
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include "Active/Utility/BufferOut.h"
#include "Speckle/Database/BIMAttributeDatabase.h"
#include "Speckle/Environment/Addon.h"
#include "Speckle/Environment/Project.h"
#include "Speckle/Utility/Guid.h"
using namespace active::serialise;
using namespace speckle::database;
using namespace speckle::environment;
using namespace speckle::record::attribute;
using namespace speckle::utility;
#include <array>
#include <memory>
namespace speckle::record::attribute {
class Storey::Data {
public:
#ifdef ARCHICAD
Data(const API_StoryType& storey) : root{storey} {}
Data(const Data& source) : root{source.root} {}
API_StoryType root;
API_Attr_Head header;
#endif
};
}
namespace {
///Serialisation fields
enum FieldIndex {
levelID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"level"},
};
#ifdef ARCHICAD
/*!
Fill in an Archicad API attribute header based on a storey
@param header The attribute header to be populated
@param storey The storey to be copied into the header
*/
void fillHeader(API_Attr_Head& header, const API_StoryType& storey) {
active::utility::Memory::erase(header);
//NB: This is not intended to be used for API attribute calls - it only transports core properties within this framework, e.g. name
header.typeID = static_cast<API_AttrTypeID>(Attribute::storeyTableID);
header.index = ACAPI_CreateAttributeIndex(storey.index);
header.guid = Guid{Guid::fromInt(storey.floorId)};
String{storey.uName}.writeUTF8(active::utility::BufferOut{header.name}, true);
}
#endif
}
/*--------------------------------------------------------------------
Default constructor
--------------------------------------------------------------------*/
Storey::Storey() {
} //Storey::Storey
#ifdef ARCHICAD
/*--------------------------------------------------------------------
Constructor
index: An index into the Archicad storey array
--------------------------------------------------------------------*/
Storey::Storey(short index) {
m_storeyIndex = index;
} //Storey::Storey
#endif
/*--------------------------------------------------------------------
Constructor
ID: The attribute ID
--------------------------------------------------------------------*/
Storey::Storey(const database::BIMRecordID& ID) : base{ID, storeyTableID} {
} //Storey::Storey
/*--------------------------------------------------------------------
Copy constructor
source: The object to copy
--------------------------------------------------------------------*/
Storey::Storey(const Storey& source) : base{source} {
m_data = source.m_data ? std::make_unique<Data>(*m_data) : nullptr;
#ifdef ARCHICAD
m_storeyIndex = source.m_storeyIndex;
#endif
} //Storey::Storey
/*--------------------------------------------------------------------
Destructor
--------------------------------------------------------------------*/
Storey::~Storey() {}
#ifdef ARCHICAD
/*--------------------------------------------------------------------
Get the (immutable) API attribute header data
return: The attribute header data (only use this data for low-level operations - for normal code, call getters/setters)
--------------------------------------------------------------------*/
const API_Attr_Head& Storey::getHead() const {
confirmData();
fillHeader(m_data->header, m_data->root);
return m_data->header;
} //Storey::getHead
/*--------------------------------------------------------------------
Get the (mutable) API attribute header data
return: The attribute header data (only use this data for low-level operations - for normal code, call getters/setters)
--------------------------------------------------------------------*/
API_Attr_Head& Storey::getHead() {
confirmData();
fillHeader(m_data->header, m_data->root);
return m_data->header;
} //Storey::getHead
#endif
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool Storey::fillInventory(Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ fieldID[levelID], levelID, element }, //TODO: implement other fields
},
}.withType(&typeid(Storey)));
return base::fillInventory(inventory);
} //Storey::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique Storey::getCargo(const Inventory::Item& item) const {
if (item.ownerType != &typeid(Storey))
return base::getCargo(item);
confirmData();
using namespace active::serialise;
switch (item.index) {
case levelID:
return std::make_unique<DoubleWrap>(m_data->root.level);
default:
return nullptr; //Requested an unknown index
}
} //Storey::getCargo
/*--------------------------------------------------------------------
Set to the default package content
--------------------------------------------------------------------*/
void Storey::setDefault() {
} //Storey::setDefault
/*--------------------------------------------------------------------
Confirm the internal data, either loading from the BIM application or setting a default
--------------------------------------------------------------------*/
void Storey::confirmData() const {
if (m_data)
return;
#ifdef ARCHICAD
m_data = std::make_unique<Data>(getStoreyData());
#endif
} //Storey::confirmData
#ifdef ARCHICAD
/*--------------------------------------------------------------------
Get the storey data from the host BIM application
return: The storey data (for internal use to populate derived classes)
--------------------------------------------------------------------*/
API_StoryType Storey::getStoreyData() const {
do {
if (auto project = addon()->getActiveProject().lock(); project) {
auto attributeDatabase = project->getAttributeDatabase();
if (m_storeyIndex) {
auto storeyID = attributeDatabase->getStoreyID(*m_storeyIndex);
m_storeyIndex.reset();
if (!storeyID)
break;
resetIndex({*storeyID, Attribute::storeyTableID});
}
if (auto storey = attributeDatabase->getAPIStorey(getBIMLink()); storey)
return *storey;
}
} while (false);
API_StoryType storey;
active::utility::Memory::erase(storey);
return storey;
} //Storey::getStoreyData
#endif
@@ -0,0 +1,135 @@
#ifndef SPECKLE_RECORD_ATTRIBUTE_STOREY
#define SPECKLE_RECORD_ATTRIBUTE_STOREY
#include "Speckle/Record/Attribute/Attribute.h"
namespace speckle::record::attribute {
/*!
A storey or level in a building
Represented in Archicad by `API_StoryType`
*/
class Storey : public Attribute {
public:
// MARK: - Types
using base = Attribute;
///Unique pointer
using Unique = std::unique_ptr<Storey>;
///Shared pointer
using Shared = std::shared_ptr<Storey>;
///Optional
using Option = std::optional<Storey>;
// MARK: - Constructors
using base::base;
/*!
Default constructor
*/
Storey();
/*!
Constructor
@param ID The attribute ID
*/
Storey(const database::BIMRecordID& ID);
#ifdef ARCHICAD
/*!
Constructor
@param index An index into the Archicad storey array
*/
Storey(short index);
/*!
Constructor
@param attrData Archicad attribute data
*/
Storey(const API_StoryType& attrData);
#endif
/*!
Copy constructor
@param source The object to copy
*/
Storey(const Storey& source);
/*!
Destructor
*/
~Storey();
/*!
Object cloning
@return A clone of this object
*/
Storey* clonePtr() const override { return new Storey{*this}; }
// MARK: - Functions (const)
/*!
Get the speckle type identifier
@return The speckle type (relevant objects should override as required)
*/
speckle::utility::String getSpeckleType() const override { return "speckle::record::attribute::Storey"; }
#ifdef ARCHICAD
/*!
Get the (immutable) API attribute header data
@return The attribute header data (only use this data for low-level operations - for normal code, call getters/setters)
*/
const API_Attr_Head& getHead() const override;
#endif
// MARK: - Functions (mutating)
#ifdef ARCHICAD
/*!
Get the (mutable) API attribute header data
@return The attribute header data (only use this data for low-level operations - for normal code, call getters/setters)
*/
API_Attr_Head& getHead() override;
#endif
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
/*!
Set to the default package content
*/
void setDefault() override;
private:
/*!
Confirm the internal data, either loading from the BIM application or setting a default
*/
void confirmData() const;
#ifdef ARCHICAD
/*!
Get the storey data from the host BIM application
@return The storey data (for internal use to populate derived classes)
*/
API_StoryType getStoreyData() const;
///An index into the Archicad storey array - used temporarily for looking up the storey data on demand
mutable std::optional<short> m_storeyIndex;
#endif
class Data;
///The attribute data - mutable to support lazy loading
mutable std::unique_ptr<Data> m_data;
};
}
#endif //SPECKLE_RECORD_ATTRIBUTE_STOREY
@@ -48,6 +48,17 @@ namespace speckle::record::cred {
// MARK: - Functions (const)
/*!
Get the account server URL
@return The account server URL (nullopt if none specified)
*/
speckle::utility::String getServerURL() const { return m_serverInfo.getURL(); }
/*!
Get the account token
@return The account token
*/
speckle::utility::String getToken() const { return m_token; }
// MARK: - Functions (mutating)
// MARK: - Serialisation
@@ -89,7 +89,7 @@ Cargo::Unique ServerInfo::getCargo(const Inventory::Item& item) const {
case frontEndID:
return std::make_unique<ValueWrap<bool>>(m_frontend2);
case urlID:
return std::make_unique<StringOptWrap>(m_url);
return std::make_unique<StringWrap>(m_url);
case migrationID:
return std::make_unique<Mover>(PackageUniqueWrap{m_migration});
default:
@@ -32,7 +32,7 @@ namespace speckle::record::cred {
*/
ServerInfo(const utility::String& name, const utility::String::Option company = std::nullopt,
const utility::String::Option version = std::nullopt, const utility::String::Option contact = std::nullopt,
const utility::String::Option description = std::nullopt, const utility::String::Option url = std::nullopt,
const utility::String::Option description = std::nullopt, const utility::String url = {},
bool isFrontEnd = false, std::unique_ptr<ServerMigration> migration = nullptr) :
m_name{name}, m_company{company}, m_version{version}, m_adminContact{contact}, m_description{description},
m_url{url}, m_frontend2{isFrontEnd}, m_migration{std::move(migration)} {}
@@ -90,7 +90,7 @@ namespace speckle::record::cred {
Get the URL
@return The URL
*/
const utility::String::Option& getURL() const { return m_url; }
const utility::String& getURL() const { return m_url; }
/*!
Get the migration history
@return The migration history (nullptr = no history)
@@ -143,7 +143,7 @@ namespace speckle::record::cred {
This field is not returned from the GQL API, it should be populated after construction.
See "Speckle.Core.Credentials.AccountManager"
*/
utility::String::Option m_url;
utility::String m_url;
///Server migration record
std::unique_ptr<ServerMigration> m_migration;
};
@@ -0,0 +1,174 @@
#include "Speckle/Record/Element/Column.h"
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include "Active/Serialise/Package/Wrapper/PackageWrap.h"
#include "Active/Serialise/Package/Wrapper/ContainerWrap.h"
#include "Speckle/Environment/Addon.h"
#include "Speckle/Primitive/Mesh/Mesh.h"
#include "Speckle/SpeckleResource.h"
#include "Speckle/Utility/Guid.h"
using namespace active::serialise;
using namespace speckle::environment;
using namespace speckle::record::attribute;
using namespace speckle::record::element;
using namespace speckle::utility;
#include <array>
#include <memory>
namespace speckle::record::element {
class Column::Data {
public:
friend class Column;
#ifdef ARCHICAD
Data(const API_Element& elem) : root{ std::make_unique<API_ColumnType>(elem.column) } {}
Data(const Data& source) : root{ std::make_unique<API_ColumnType>(*source.root) } {}
#endif
private:
std::unique_ptr<API_ColumnType> root;
};
}
namespace {
///Serialisation fields
enum FieldIndex {
segmentID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"segments"},
};
}
/*--------------------------------------------------------------------
Default constructor
--------------------------------------------------------------------*/
Column::Column() {
} //Column::Column
#ifdef ARCHICAD
/*--------------------------------------------------------------------
Constructor
elemData: Archicad element data
tableID: The element table ID (AC database, e.g. floor plan, 3D)
--------------------------------------------------------------------*/
Column::Column(const API_Element& elemData, const speckle::utility::Guid& tableID) : base{ elemData.header.guid, tableID } {
m_data = std::make_unique<Data>(elemData);
} //Column::Column
#endif
/*--------------------------------------------------------------------
Copy constructor
source: The object to copy
--------------------------------------------------------------------*/
Column::Column(const Column& source) : base{ source } {
m_data = source.m_data ? std::make_unique<Data>(*m_data) : nullptr;
} //Column::Column
/*--------------------------------------------------------------------
Destructor
--------------------------------------------------------------------*/
Column::~Column() {}
#ifdef ARCHICAD
/*--------------------------------------------------------------------
Get the (immutable) API element header data
return: The element header data (only use this data for low-level operations - for normal code, call getters/setters)
--------------------------------------------------------------------*/
const API_Elem_Head& Column::getHead() const {
return m_data->root->head;
} //Column::getHead
/*--------------------------------------------------------------------
Get the (mutable) API element header data
return: The element header data (only use this data for low-level operations - for normal code, call getters/setters)
--------------------------------------------------------------------*/
API_Elem_Head& Column::getHead() {
return m_data->root->head;
} //Column::getHead
/*--------------------------------------------------------------------
Load the element memo structure (elements must override according to requirements)
filter: Filter bits specifying memo requirements
--------------------------------------------------------------------*/
void Column::loadMemo(filter_bits filter, std::unique_ptr<Memo>& memo) const {
//Establish the memo filter for this element
if (!SegmentedColumn::isMemoLoaded())
filter |= SegmentedColumn::getPartFilter();
Element::loadMemo(filter, memo);
//Receive the memo data into the element (when available)
if (memo) {
if (filter & SegmentedColumn::getPartFilter())
SegmentedColumn::receive(*memo);
}
SegmentedColumn::setMemoLoaded(true); //Always mark the data as loaded to prevent repeated attempts on error
} //Column::loadMemo
#endif
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool Column::fillInventory(Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ fieldID[segmentID], segmentID, getSegmentCount(), std::nullopt }, //TODO: implement other fields
},
}.withType(&typeid(Column)));
return base::fillInventory(inventory);
} //Column::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique Column::getCargo(const Inventory::Item& item) const {
if (item.ownerType != &typeid(Column))
return base::getCargo(item);
using namespace active::serialise;
switch (item.index) {
case segmentID:
if (auto segment = getSegment(item.available); segment != nullptr) {
return Cargo::Unique{new PackageWrap{*segment}};
} else
return nullptr;
default:
return nullptr; //Requested an unknown index
}
} //Column::getCargo
/*--------------------------------------------------------------------
Set to the default package content
--------------------------------------------------------------------*/
void Column::setDefault() {
base::setDefault();
m_data.reset();
} //Column::setDefault
+142
View File
@@ -0,0 +1,142 @@
#ifndef SPECKLE_RECORD_ELEMENT_COLUMN
#define SPECKLE_RECORD_ELEMENT_COLUMN
#include "Speckle/Record/Element/ColumnSegment.h"
#include "Speckle/Record/Element/Element.h"
#include "Speckle/Record/Element/Interface/Assembly/Path.h"
#include "Speckle/Record/Element/Interface/SegmentedColumn.h"
namespace speckle::record::element {
class ColumnSegment;
/*!
BIM column class
*/
class Column : public Element, public SegmentedColumn, public assembly::Path {
public:
// MARK: - Types
using base = Element;
///Unique pointer
using Unique = std::unique_ptr<Column>;
///Shared pointer
using Shared = std::shared_ptr<Column>;
///Optional
using Option = std::optional<Column>;
// MARK: - Constructors
using base::base;
/*!
Default constructor
*/
Column();
#ifdef ARCHICAD
/*!
Constructor
@param elemData Archicad element data
@param tableID The column element ID (AC database, e.g. floor plan, 3D)
*/
Column(const API_Element& elemData, const speckle::utility::Guid& tableID);
#endif
/*!
Copy constructor
@param source The object to copy
*/
Column(const Column& source);
/*!
Destructor
*/
~Column();
/*!
Object cloning
@return A clone of this object
*/
Column* clonePtr() const override { return new Column{*this}; }
// MARK: - Functions (const)
/*!
Get the speckle type identifier
@return The speckle type (relevant objects should override as required)
*/
speckle::utility::String getSpeckleType() const override { return "Objects.BuiltElements.Element:Objects.BuiltElements.Column"; }
/*!
Get the BIM application parent table ID
@return The BIM table ID
*/
virtual database::BIMRecordID getTableID() const override { return Element::getTableID(); }
/*!
Get the element body
@return nullptr (Columns don't explicitly have a 3D body - this comes from its child segments)
*/
virtual Body* getBody() const override { return nullptr; }
/*!
Get the number of segments in the path (elements must override according to requirements)
@return The segment count
*/
virtual size_t getSegmentCount() const override { return SegmentedColumn::getSegmentCount(); }
/*!
Get a segment from the path (elements must override according to requirements)
@param index The index of the required segment
@return The requested segment (nullptr on failure)
*/
virtual ColumnSegment* getSegment(size_t index) const override { return SegmentedColumn::getSegment(index); }
#ifdef ARCHICAD
/*!
Get the (immutable) API element header data
@return The element header data (only use this data for low-level operations - for normal code, call getters/setters)
*/
const API_Elem_Head& getHead() const override;
#endif
// MARK: - Functions (mutating)
#ifdef ARCHICAD
/*!
Get the (mutable) API element header data
@return The element header data (only use this data for low-level operations - for normal code, call getters/setters)
*/
API_Elem_Head& getHead() override;
#endif
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
/*!
Set to the default package content
*/
void setDefault() override;
protected:
/*!
Load the element memo structure (elements must override according to requirements)
@param filter Filter bits specifying memo requirements
*/
virtual void loadMemo(filter_bits filter, std::unique_ptr<Memo>& memo) const override;
private:
class Data;
///The column data
std::unique_ptr<Data> m_data;
};
}
#endif //SPECKLE_RECORD_ELEMENT_COLUMN
@@ -0,0 +1,159 @@
#include "Speckle/Record/Element/ColumnSegment.h"
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include "Active/Serialise/Package/Wrapper/PackageWrap.h"
#include "Active/Serialise/Package/Wrapper/ContainerWrap.h"
#include "Speckle/Environment/Addon.h"
#include "Speckle/Primitive/Mesh/Mesh.h"
#include "Speckle/SpeckleResource.h"
#include "Speckle/Utility/Guid.h"
#ifdef ARCHICAD
#include <ACAPinc.h>
#endif
using namespace active::serialise;
using namespace speckle::environment;
using namespace speckle::record::attribute;
using namespace speckle::record::element;
using namespace speckle::utility;
#include <array>
#include <memory>
namespace speckle::record::element {
class ColumnSegment::Data {
public:
friend class ColumnSegment;
#ifdef ARCHICAD
Data(const API_ColumnSegmentType& seg) : root{ std::make_unique<API_ColumnSegmentType>(seg) } {}
Data(const Data& source) : root{ std::make_unique<API_ColumnSegmentType>(*source.root) } {}
private:
std::unique_ptr<API_ColumnSegmentType> root;
#endif
};
}
/*--------------------------------------------------------------------
Default constructor
--------------------------------------------------------------------*/
ColumnSegment::ColumnSegment() {
} //ColumnSegment::ColumnSegment
#ifdef ARCHICAD
/*--------------------------------------------------------------------
Constructor
elemData: Archicad element data
tableID: The element table ID (AC database, e.g. floor plan, 3D)
--------------------------------------------------------------------*/
ColumnSegment::ColumnSegment(const API_Element& elemData, const speckle::utility::Guid& tableID) : base{ elemData.header.guid, tableID } {
m_data = std::make_unique<Data>(elemData.columnSegment);
} //ColumnSegment::ColumnSegment
/*--------------------------------------------------------------------
Constructor
segment: The segment element data
tableID: The parent table ID
cutOrigin: Cut at the segment origin
cutEnd: Cut at the segment end
scheme: The segment scheme
profile: The segment profile (nullptr = none)
--------------------------------------------------------------------*/
ColumnSegment::ColumnSegment(const API_ColumnSegmentType& segment, const speckle::utility::Guid& tableID, const API_AssemblySegmentCutData& cutOrigin,
const API_AssemblySegmentCutData& cutEnd, const API_AssemblySegmentSchemeData& scheme,
const API_AssemblySegmentProfileData* profile) :
base{segment.head.guid, tableID}, assembly::Segment{cutOrigin, cutEnd, scheme, profile} {
m_data = std::make_unique<Data>(segment);
} //ColumnSegment::ColumnSegment
#endif
/*--------------------------------------------------------------------
Copy constructor
source: The object to copy
--------------------------------------------------------------------*/
ColumnSegment::ColumnSegment(const ColumnSegment& source) : base{ source } {
m_data = source.m_data ? std::make_unique<Data>(*m_data) : nullptr;
} //ColumnSegment::ColumnSegment
/*--------------------------------------------------------------------
Move constructor
source: The object to move
--------------------------------------------------------------------*/
ColumnSegment::ColumnSegment(ColumnSegment&& source) : base{source} {
m_data = std::move(source.m_data);
} //ColumnSegment::ColumnSegment
/*--------------------------------------------------------------------
Destructor
--------------------------------------------------------------------*/
ColumnSegment::~ColumnSegment() {}
#ifdef ARCHICAD
/*--------------------------------------------------------------------
Get the (immutable) API element header data
return: The element header data (only use this data for low-level operations - for normal code, call getters/setters)
--------------------------------------------------------------------*/
const API_Elem_Head& ColumnSegment::getHead() const {
return m_data->root->head;
} //ColumnSegment::getHead
/*--------------------------------------------------------------------
Get the (mutable) API element header data
return: The element header data (only use this data for low-level operations - for normal code, call getters/setters)
--------------------------------------------------------------------*/
API_Elem_Head& ColumnSegment::getHead() {
return m_data->root->head;
} //ColumnSegment::getHead
#endif
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool ColumnSegment::fillInventory(Inventory& inventory) const {
using enum Entry::Type;
//TODO: Implement other fields as required
return base::fillInventory(inventory);
} //ColumnSegment::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique ColumnSegment::getCargo(const Inventory::Item& item) const {
//TODO: Implement other fields as required
return base::getCargo(item);
} //ColumnSegment::getCargo
/*--------------------------------------------------------------------
Set to the default package content
--------------------------------------------------------------------*/
void ColumnSegment::setDefault() {
m_data.reset();
} //ColumnSegment::setDefault
@@ -0,0 +1,135 @@
#ifndef SPECKLE_RECORD_ELEMENT_COLUMN_SEGMENT
#define SPECKLE_RECORD_ELEMENT_COLUMN_SEGMENT
#include "Speckle/Record/Element/Element.h"
#include "Speckle/Record/Element/Interface/Assembly/Segment.h"
namespace speckle::record::element {
class SegmentedColumn;
/*!
BIM column class
*/
class ColumnSegment : public Element, public assembly::Segment {
public:
// MARK: - Types
using base = Element;
///Unique pointer
using Unique = std::unique_ptr<ColumnSegment>;
///Shared pointer
using Shared = std::shared_ptr<ColumnSegment>;
///Optional
using Option = std::optional<ColumnSegment>;
// MARK: - Constructors
using base::base;
/*!
Default constructor
*/
ColumnSegment();
#ifdef ARCHICAD
/*!
Constructor
@param elemData Archicad element data
@param tableID The element table ID (AC database, e.g. floor plan, 3D)
*/
ColumnSegment(const API_Element& elemData, const speckle::utility::Guid& tableID);
#endif
/*!
Copy constructor
@param source The object to copy
*/
ColumnSegment(const ColumnSegment& source);
/*!
Move constructor
@param source The object to move
*/
ColumnSegment(ColumnSegment&& source);
/*!
Destructor
*/
~ColumnSegment();
/*!
Object cloning
@return A clone of this object
*/
ColumnSegment* clonePtr() const override { return new ColumnSegment{*this}; }
// MARK: - Functions (const)
/*!
Get the speckle type identifier
@return The speckle type (relevant objects should override as required)
*/
speckle::utility::String getSpeckleType() const override { return "Objects.BuiltElements.Element:Objects.BuiltElements.ColumnSegment"; }
#ifdef ARCHICAD
/*!
Get the (immutable) API element header data
@return The element header data (only use this data for low-level operations - for normal code, call getters/setters)
*/
const API_Elem_Head& getHead() const override;
#endif
// MARK: - Functions (mutating)
#ifdef ARCHICAD
/*!
Get the (mutable) API element header data
@return The element header data (only use this data for low-level operations - for normal code, call getters/setters)
*/
API_Elem_Head& getHead() override;
#endif
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
/*!
Set to the default package content
*/
void setDefault() override;
protected:
friend class SegmentedColumn;
#ifdef ARCHICAD
/*!
Constructor
@param segment The segment element data
@param tableID The parent table ID
@param cutOrigin Cut at the segment origin
@param cutEnd Cut at the segment end
@param scheme The segment scheme
@param profile The segment profile (nullptr = none)
*/
ColumnSegment(const API_ColumnSegmentType& segment, const speckle::utility::Guid& tableID, const API_AssemblySegmentCutData& cutOrigin,
const API_AssemblySegmentCutData& cutEnd, const API_AssemblySegmentSchemeData& scheme,
const API_AssemblySegmentProfileData* profile = nullptr);
#endif
private:
class Data;
///The column data
std::unique_ptr<Data> m_data;
};
}
#endif //SPECKLE_RECORD_ELEMENT_COLUMN_SEGMENT
@@ -0,0 +1,293 @@
#include "Speckle/Record/Element/Element.h"
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include "Active/Serialise/Package/Wrapper/PackageWrap.h"
#include "Active/Serialise/Package/Wrapper/ContainerWrap.h"
#include "Speckle/Database/BIMElementDatabase.h"
#include "Speckle/Environment/Addon.h"
#include "Speckle/Environment/Project.h"
#include "Speckle/Primitive/Mesh/Mesh.h"
#include "Speckle/Record/Element/Memo.h"
#include "Speckle/SpeckleResource.h"
#include "Speckle/Utility/Guid.h"
#ifdef ARCHICAD
#include <Sight.hpp>
#include <Model.hpp>
#include <ModelMaterial.hpp>
#include <ModelElement.hpp>
#include <exp.h>
#include <ModelMeshBody.hpp>
#include <ConvexPolygon.hpp>
#endif
using namespace active::serialise;
using namespace speckle::environment;
using namespace speckle::record::attribute;
using namespace speckle::record::element;
using namespace speckle::utility;
#include <array>
#include <memory>
namespace speckle::record::element {
class Element::Data {
public:
friend class Element;
Data() {}
Data(const Data& source) {}
private:
std::unique_ptr<Element::Body> m_cache;
};
}
namespace {
///Serialisation fields
enum FieldIndex {
bodyID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"displayValue"},
};
}
/*--------------------------------------------------------------------
Default constructor
--------------------------------------------------------------------*/
Element::Element() {
} //Element::Element
/*--------------------------------------------------------------------
Constructor
ID: The record ID
tableID: The parent table ID
unit: The record unit type
--------------------------------------------------------------------*/
Element::Element(const Guid& ID, const Guid& tableID, std::optional<active::measure::LengthType> unit) : base{ID, tableID, unit} {
m_data = std::make_unique<Data>();
} //Element::Element
/*--------------------------------------------------------------------
Copy constructor
source: The object to copy
--------------------------------------------------------------------*/
Element::Element(const Element& source) : base{ source } {
m_data = source.m_data ? std::make_unique<Data>(*m_data) : nullptr;
} //Element::Element
/*--------------------------------------------------------------------
Move constructor
source: The object to move
--------------------------------------------------------------------*/
Element::Element(Element&& source) : base{source} {
m_data = std::move(source.m_data);
} //Element::Element
/*--------------------------------------------------------------------
Destructor
--------------------------------------------------------------------*/
Element::~Element() {}
/*--------------------------------------------------------------------
Get the element storey
return: The element storey (nullopt if the element isn't linked to a storey)
--------------------------------------------------------------------*/
Storey::Option Element::getStorey() const {
#ifdef ARCHICAD
return Storey{ getHead().floorInd };
#endif
} //Element::getStorey
/*--------------------------------------------------------------------
Get the element type name, e.g. "Wall", "Roof" etc
return: The type name
--------------------------------------------------------------------*/
String Element::getTypeName() const {
#ifdef ARCHICAD
GS::UniString typeName;
if (auto err = ACAPI_Element_GetElemTypeName(getHead().type, typeName); err != NoError)
return addon()->getLocalString(titleStringLib, unknownElementTypeID);
return typeName;
#endif
} //Element::getTypeName
/*--------------------------------------------------------------------
Get the element body as a list of faces or Meshes
return: A pointer to the element body
--------------------------------------------------------------------*/
Element::Body* Element::getBody() const {
#ifdef ARCHICAD
if (m_data->m_cache) {
return m_data->m_cache.get();
}
void* dummy = nullptr;
GSErrCode err = ACAPI_Sight_GetCurrentWindowSight(&dummy);
if (err != NoError)
{
// TODO: should this throw?
}
Modeler::SightPtr currentSightPtr((Modeler::Sight*)dummy); // init the shared ptr with the raw pointer
ModelerAPI::Model acModel;
Modeler::IAttributeReader* attrReader = ACAPI_Attribute_GetCurrentAttributeSetReader();
err = EXPGetModel(currentSightPtr, &acModel, attrReader);
if (err != NoError)
{
// TODO: should this throw?
}
auto elementBody = new Element::Body();
Int32 nElements = acModel.GetElementCount();
for (Int32 iElement = 1; iElement <= nElements; iElement++)
{
ModelerAPI::Element elem{};
acModel.GetElement(iElement, &elem);
if (elem.GetElemGuid() != getHead().guid)
continue;
Int32 nBodies = elem.GetTessellatedBodyCount();
for (Int32 bodyIndex = 1; bodyIndex <= nBodies; ++bodyIndex)
{
ModelerAPI::MeshBody body{};
elem.GetTessellatedBody(bodyIndex, &body);
Int32 polyCount = body.GetPolygonCount();
for (Int32 polyIndex = 1; polyIndex <= polyCount; ++polyIndex)
{
ModelerAPI::Polygon polygon{};
body.GetPolygon(polyIndex, &polygon);
ModelerAPI::Material material{};
polygon.GetMaterial(&material);
Int32 convexPolyCount = polygon.GetConvexPolygonCount();
for (Int32 convPolyIndex = 1; convPolyIndex <= convexPolyCount; ++convPolyIndex)
{
std::vector<double> vertices;
std::vector<int> faces;
std::vector<int> colors;
ModelerAPI::ConvexPolygon convexPolygon{};
polygon.GetConvexPolygon(convPolyIndex, &convexPolygon);
Int32 vertexCount = convexPolygon.GetVertexCount();
faces.push_back(vertexCount);
for (Int32 vertexIndex = 1; vertexIndex <= vertexCount; ++vertexIndex)
{
ModelerAPI::Vertex vertex{};
body.GetVertex(convexPolygon.GetVertexIndex(vertexIndex), &vertex);
// TODO: change vertices array to hold Vertex instead of double values
vertices.push_back(vertex.x);
vertices.push_back(vertex.y);
vertices.push_back(vertex.z);
//double alpha = material.GetTransparency();
//ModelerAPI::Color color = material.GetSurfaceColor();
//colors.push_back(ARGBToInt(alpha, color.red, color.green, color.blue));
faces.push_back(vertexIndex - 1);
}
elementBody->push_back(primitive::Mesh(std::move(vertices), std::move(faces), std::move(colors), material));
}
}
}
}
m_data->m_cache.reset(elementBody);
return m_data->m_cache.get();
#endif
}
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool Element::fillInventory(Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ fieldID[bodyID], bodyID, element }, //TODO: implement other fields
},
}.withType(&typeid(Element)));
return base::fillInventory(inventory);
} //Element::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique Element::getCargo(const Inventory::Item& item) const {
if (item.ownerType != &typeid(Element))
return base::getCargo(item);
using namespace active::serialise;
switch (item.index) {
case bodyID:
if (auto body = getBody(); body != nullptr)
{
return Cargo::Unique{ new active::serialise::ContainerWrap{*body} };
}
else
return nullptr;
default:
return nullptr; //Requested an unknown index
}
} //Element::getCargo
/*--------------------------------------------------------------------
Set to the default package content
--------------------------------------------------------------------*/
void Element::setDefault() {
base::setDefault();
m_data.reset();
} //Element::setDefault
/*--------------------------------------------------------------------
Load the element memo structure (elements must override according to requirements)
filter: Filter bits specifying memo requirements
--------------------------------------------------------------------*/
void Element::loadMemo(Part::filter_bits filter, std::unique_ptr<Memo>& memo) const {
//If the memo data isn't loaded, fetch it now
if (!memo) {
auto project = addon()->getActiveProject().lock();
if (!project)
return;
if (auto loaded = project->getElementDatabase()->getMemo(getBIMID(), filter); loaded)
memo.reset(loaded.release());
}
} //Element::loadMemo
+139
View File
@@ -0,0 +1,139 @@
#ifndef SPECKLE_RECORD_ELEMENT
#define SPECKLE_RECORD_ELEMENT
#include "Speckle/Database/Content/BIMRecord.h"
#include "Speckle/Record/Element/Interface/Part.h"
#include "Speckle/Record/Attribute/Storey.h"
#include "Speckle/Utility/String.h"
namespace speckle::primitive {
class Mesh;
}
namespace speckle::record::element {
class Memo;
/*!
Base BIM element class
*/
class Element : public speckle::database::BIMRecord {
public:
///An element 3D body primitive
using Body = std::vector<primitive::Mesh>;
// MARK: - Types
using base = speckle::database::BIMRecord;
///Unique pointer
using Unique = std::unique_ptr<Element>;
///Shared pointer
using Shared = std::shared_ptr<Element>;
///Optional
using Option = std::optional<Element>;
// MARK: - Constructors
/*!
Default constructor
*/
Element();
/*!
Constructor
@param ID The record ID
@param tableID The parent table ID
@param unit The record unit type
*/
Element(const speckle::utility::Guid& ID, const speckle::utility::Guid& tableID,
std::optional<active::measure::LengthType> unit = active::measure::LengthType::metre);
/*!
Copy constructor
@param source The object to copy
*/
Element(const Element& source);
/*!
Move constructor
@param source The object to move
*/
Element(Element&& source);
/*!
Destructor
*/
~Element();
// MARK: - Functions (const)
/*!
Get the speckle type identifier
@return The speckle type (relevant objects should override as required)
*/
virtual speckle::utility::String getSpeckleType() const override { return "Objects.BuiltElements.Element:Objects.BuiltElements.Element"; }
/*!
Get the elmeent type name, e.g. "Wall", "Roof" etc
@return The type name
*/
virtual speckle::utility::String getTypeName() const;
/*!
Get the element storey
@return The element storey (nullopt if the element isn't linked to a storey)
*/
virtual attribute::Storey::Option getStorey() const;
/*!
Get the element body
@return An array of meshes from the element body (nullptr if no body data is available)
*/
virtual Body* getBody() const;
#ifdef ARCHICAD
/*!
Get the (immutable) API element header data
@return The element header data (only use this data for low-level operations - for normal code, call getters/setters)
*/
virtual const API_Elem_Head& getHead() const = 0;
#endif
// MARK: - Functions (mutating)
#ifdef ARCHICAD
/*!
Get the (mutable) API element header data
@return The element header data (only use this data for low-level operations - for normal code, call getters/setters)
*/
virtual API_Elem_Head& getHead() = 0;
#endif
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
/*!
Set to the default package content
*/
void setDefault() override;
protected:
/*!
Load the element memo structure (elements must override according to requirements)
@param filter Filter bits specifying memo requirements
*/
virtual void loadMemo(Part::filter_bits filter, std::unique_ptr<Memo>& memo) const;
private:
class Data;
///The element data
std::unique_ptr<Data> m_data;
};
}
#endif //SPECKLE_RECORD_ELEMENT
@@ -0,0 +1,157 @@
#include "Speckle/Record/Element/GenericElement.h"
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include "Active/Serialise/Package/Wrapper/PackageWrap.h"
#include "Active/Serialise/Package/Wrapper/ContainerWrap.h"
#include "Speckle/Environment/Addon.h"
#include "Speckle/Primitive/Mesh/Mesh.h"
#include "Speckle/SpeckleResource.h"
#include "Speckle/Utility/Guid.h"
using namespace active::serialise;
using namespace speckle::environment;
using namespace speckle::record::attribute;
using namespace speckle::record::element;
using namespace speckle::utility;
#include <array>
#include <memory>
namespace speckle::record::element {
class GenericElement::Data {
public:
friend class GenericElement;
#ifdef ARCHICAD
Data(const API_Element& elem) : root{ std::make_unique<API_Element>(elem) } {}
Data(const Data& source) : root{ std::make_unique<API_Element>(*source.root) } {}
#endif
private:
std::unique_ptr<API_Element> root;
std::unique_ptr<GenericElement::Body> m_cache;
};
}
namespace {
///Serialisation fields
enum FieldIndex {
bodyID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"displayValue"},
};
}
/*--------------------------------------------------------------------
Default constructor
--------------------------------------------------------------------*/
GenericElement::GenericElement() {
} //GenericElement::GenericElement
/*--------------------------------------------------------------------
Constructor
elemData: Archicad element data
tableID: The attribute table ID (attribute type)
--------------------------------------------------------------------*/
GenericElement::GenericElement(const API_Element& elemData, const speckle::utility::Guid& tableID) : base{ elemData.header.guid, tableID } {
m_data = std::make_unique<Data>(elemData);
} //GenericElement::GenericElement
/*--------------------------------------------------------------------
Copy constructor
source: The object to copy
--------------------------------------------------------------------*/
GenericElement::GenericElement(const GenericElement& source) : base{ source } {
m_data = source.m_data ? std::make_unique<Data>(*m_data) : nullptr;
} //GenericElement::GenericElement
/*--------------------------------------------------------------------
Destructor
--------------------------------------------------------------------*/
GenericElement::~GenericElement() {}
#ifdef ARCHICAD
/*--------------------------------------------------------------------
Get the (immutable) API element header data
return: The element header data (only use this data for low-level operations - for normal code, call getters/setters)
--------------------------------------------------------------------*/
const API_Elem_Head& GenericElement::getHead() const {
return m_data->root->header;
} //GenericElement::getHead
/*--------------------------------------------------------------------
Get the (mutable) API element header data
return: The element header data (only use this data for low-level operations - for normal code, call getters/setters)
--------------------------------------------------------------------*/
API_Elem_Head& GenericElement::getHead() {
return m_data->root->header;
} //GenericElement::getHead
#endif
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool GenericElement::fillInventory(Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ fieldID[bodyID], bodyID, element }, //TODO: implement other fields
},
}.withType(&typeid(GenericElement)));
return base::fillInventory(inventory);
} //GenericElement::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique GenericElement::getCargo(const Inventory::Item& item) const {
if (item.ownerType != &typeid(GenericElement))
return base::getCargo(item);
using namespace active::serialise;
switch (item.index) {
case bodyID:
if (auto body = getBody(); body != nullptr)
{
return Cargo::Unique{ new active::serialise::ContainerWrap{*body} };
}
else
return nullptr;
default:
return nullptr; //Requested an unknown index
}
} //GenericElement::getCargo
/*--------------------------------------------------------------------
Set to the default package content
--------------------------------------------------------------------*/
void GenericElement::setDefault() {
base::setDefault();
m_data.reset();
} //GenericElement::setDefault
@@ -0,0 +1,112 @@
#ifndef SPECKLE_RECORD_GENERIC_ELEMENT
#define SPECKLE_RECORD_GENERIC_ELEMENT
#include "Speckle/Record/Element/Element.h"
namespace speckle::record::element {
/*!
Catch-all class for elements that are not represented by a specific class
*/
class GenericElement : public Element {
public:
///An element 3D body primitive
using Body = std::vector<primitive::Mesh>;
// MARK: - Types
using base = Element;
///Unique pointer
using Unique = std::unique_ptr<GenericElement>;
///Shared pointer
using Shared = std::shared_ptr<GenericElement>;
///Optional
using Option = std::optional<GenericElement>;
// MARK: - Constructors
using base::base;
/*!
Default constructor
*/
GenericElement();
#ifdef ARCHICAD
/*!
Constructor
@param elemData Archicad element data
@param tableID The element table ID (AC database, e.g. floor plan, 3D)
*/
GenericElement(const API_Element& elemData, const speckle::utility::Guid& tableID);
#endif
/*!
Copy constructor
@param source The object to copy
*/
GenericElement(const GenericElement& source);
/*!
Destructor
*/
~GenericElement();
/*!
Object cloning
@return A clone of this object
*/
GenericElement* clonePtr() const override { return new GenericElement{*this}; }
// MARK: - Functions (const)
/*!
Get the speckle type identifier
@return The speckle type (relevant objects should override as required)
*/
virtual speckle::utility::String getSpeckleType() const override { return "Objects.BuiltElements.GenericElement:Objects.BuiltElements.GenericElement"; }
#ifdef ARCHICAD
/*!
Get the (immutable) API element header data
@return The element header data (only use this data for low-level operations - for normal code, call getters/setters)
*/
virtual const API_Elem_Head& getHead() const override;
#endif
// MARK: - Functions (mutating)
#ifdef ARCHICAD
/*!
Get the (mutable) API element header data
@return The element header data (only use this data for low-level operations - for normal code, call getters/setters)
*/
virtual API_Elem_Head& getHead() override;
#endif
// MARK: - Serialisation
/*!
Fill an inventory with the package items
@param inventory The inventory to receive the package items
@return True if the package has added items to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
/*!
Set to the default package content
*/
void setDefault() override;
private:
class Data;
///The element data
std::unique_ptr<Data> m_data;
};
}
#endif //SPECKLE_RECORD_GENERIC_ELEMENT
@@ -0,0 +1,23 @@
#include "Speckle/Record/Element/Interface/Assembly/Path.h"
#include "Active/Geometry/PolyEdge.h"
#ifdef ARCHICAD
#include <ACAPinc.h>
#endif
using namespace active::geometry;
using namespace speckle::database;
using namespace speckle::record::element::assembly;
/*--------------------------------------------------------------------
Get a specified edge from the segmented path
index: The index of the required edge
return: The requested edge (nullptr on failure)
--------------------------------------------------------------------*/
std::unique_ptr<active::geometry::PolyEdge> Path::getSegmentEdge(const database::BIMIndex& index) const {
//TODO: Complete when required
return nullptr;
}
@@ -0,0 +1,48 @@
#ifndef SPECKLE_RECORD_ELEMENT_ASSEMBLY_PATH
#define SPECKLE_RECORD_ELEMENT_ASSEMBLY_PATH
#include "Speckle/Database/Identity/BIMIndex.h"
#include <memory>
#include <optional>
namespace active::geometry {
class PolyEdge;
}
namespace speckle::record::element::assembly {
class Segment;
/*!
Interface for assemblies forming a path made up of a consecutive series of segments, e.g. a portal frame made from a series of beams
*/
class Path {
public:
// MARK: - Functions (const)
/*!
Get a specified edge from the segmented path
@param index The index of the required edge
@return The requested edge (nullptr on failure)
*/
std::unique_ptr<active::geometry::PolyEdge> getSegmentEdge(const database::BIMIndex& index) const;
protected:
/*!
Get the number of segments in the path (elements must override according to requirements)
@return The segment count
*/
virtual size_t getSegmentCount() const = 0;
/*!
Get a segment from the path (elements must override according to requirements)
@param index The index of the required segment
@return The requested segment (nullptr on failure)
*/
virtual Segment* getSegment(size_t index) const = 0;
};
}
#endif //SPECKLE_RECORD_ELEMENT_ASSEMBLY_PATH
@@ -0,0 +1,67 @@
#include "Speckle/Record/Element/Interface/Assembly/Segment.h"
#include "Active/Geometry/PolyEdge.h"
#include "Active/Utility/Memory.h"
#ifdef ARCHICAD
#include <ACAPinc.h>
#include <APIdefs_Elements.h>
#include <ProfileVectorImage.hpp>
#endif
using namespace active::geometry;
using namespace speckle::record::element::assembly;
/*--------------------------------------------------------------------
Default constructor
--------------------------------------------------------------------*/
Segment::Segment() :
m_cutOrigin{std::make_unique<API_AssemblySegmentCutData>()},
m_cutEnd{std::make_unique<API_AssemblySegmentCutData>()},
m_scheme{std::make_unique<API_AssemblySegmentSchemeData>()} {
} //Segment::Segment
/*--------------------------------------------------------------------
Constructor
cutOrigin: Cut at the segment origin
cutEnd: Cut at the segment end
scheme: The segment scheme
profile: The segment profile (nullptr = none)
--------------------------------------------------------------------*/
Segment::Segment(const API_AssemblySegmentCutData& cutOrigin, const API_AssemblySegmentCutData& cutEnd, const API_AssemblySegmentSchemeData& scheme,
const API_AssemblySegmentProfileData* profile) :
m_cutOrigin{std::make_unique<API_AssemblySegmentCutData>(cutOrigin)},
m_cutEnd{std::make_unique<API_AssemblySegmentCutData>(cutEnd)},
m_scheme{std::make_unique<API_AssemblySegmentSchemeData>(scheme)} {
if (profile != nullptr) {
if (profile->customOrigProfile != nullptr)
m_customProfile = std::make_unique<ProfileVectorImage>(*profile->customOrigProfile);
if (profile->stretchedProfile != nullptr)
m_stretchedProfile = std::make_unique<ProfileVectorImage>(*profile->stretchedProfile);
}
} //Segment::Segment
/*--------------------------------------------------------------------
Copy constructor
source: The object to copy
--------------------------------------------------------------------*/
Segment::Segment(const Segment& source) :
m_cutOrigin{std::make_unique<API_AssemblySegmentCutData>(*source.m_cutOrigin)},
m_cutEnd{std::make_unique<API_AssemblySegmentCutData>(*source.m_cutEnd)},
m_scheme{std::make_unique<API_AssemblySegmentSchemeData>(*source.m_scheme)},
m_edge{source.m_edge ? std::make_unique<PolyEdge>(*source.m_edge) : nullptr},
m_customProfile{source.m_customProfile ? std::make_unique<ProfileVectorImage>(*source.m_customProfile) : nullptr},
m_stretchedProfile{source.m_stretchedProfile ? std::make_unique<ProfileVectorImage>(*source.m_stretchedProfile) : nullptr} {
m_path = source.m_path;
} //Segment::Segment
/*--------------------------------------------------------------------
Destructor
--------------------------------------------------------------------*/
Segment::~Segment() {}
@@ -0,0 +1,91 @@
#ifndef SPECKLE_RECORD_ELEMENT_ASSEMBLY_SEGMENT
#define SPECKLE_RECORD_ELEMENT_ASSEMBLY_SEGMENT
#include "Speckle/Database/Identity/BIMIndex.h"
#include <memory>
#include <optional>
#ifdef ARCHICAD
struct API_AssemblySegmentCutData;
struct API_AssemblySegmentSchemeData;
class ProfileVectorImage;
#endif
namespace active::geometry {
class PolyEdge;
}
namespace speckle::record::element::assembly {
class Path;
/*!
Interface for elements forming a path made up of a consecutive series of segments, e.g. a portal frame made from a series of beams
*/
class Segment {
public:
/*!
Default constructor
*/
Segment();
/*!
Copy constructor
@param source The object to copy
*/
Segment(const Segment& source);
/*!
Destructor
*/
~Segment();
// MARK: - Functions (const)
/*!
Get the segment path
@return The segment path (nullptr = undefined)
*/
const assembly::Path* getPath() const { return m_path; }
// MARK: - Functions (mutating)
/*!
Set the segment path
@param path The segment path
*/
void setPath(const assembly::Path* path) { m_path = path; }
protected:
/*!
Constructor
@param cutOrigin Cut at the segment origin
@param cutEnd Cut at the segment end
@param scheme The segment scheme
@param profile The segment profile (nullptr = none)
*/
Segment(const API_AssemblySegmentCutData& cutOrigin, const API_AssemblySegmentCutData& cutEnd, const API_AssemblySegmentSchemeData& scheme,
const API_AssemblySegmentProfileData* profile = nullptr);
private:
#ifdef ARCHICAD
//NB: The following properties are mutable to support lazy loading
///Cut at the segment origin
mutable std::unique_ptr<API_AssemblySegmentCutData> m_cutOrigin;
///Cut at the segment end
mutable std::unique_ptr<API_AssemblySegmentCutData> m_cutEnd;
///The segment scheme
mutable std::unique_ptr<API_AssemblySegmentSchemeData> m_scheme;
///An optional custom profile (nullptr = none)
mutable std::unique_ptr<ProfileVectorImage> m_customProfile;
///An optional stretched profile (nullptr = none)
mutable std::unique_ptr<ProfileVectorImage> m_stretchedProfile;
#endif
//The segment edge
mutable std::unique_ptr<active::geometry::PolyEdge> m_edge;
//The segment path
mutable const assembly::Path* m_path = nullptr;
};
}
#endif //SPECKLE_RECORD_ELEMENT_ASSEMBLY_SEGMENT
@@ -0,0 +1,23 @@
#include "Speckle/Record/Element/Interface/Part.h"
#include "Speckle/Record/Element/Memo.h"
using namespace active::serialise;
using namespace speckle::record::attribute;
using namespace speckle::record::element;
using namespace speckle::utility;
/*--------------------------------------------------------------------
Confirm that the element part data is loaded and valid (elements must override according to requirements)
filter: Filter bits specifying memo requirements
return: True if the part data is ready to use
--------------------------------------------------------------------*/
bool Part::confirmPart(filter_bits filter) const {
if (!isMemoLoaded()) {
std::unique_ptr<Memo> memo;
loadMemo(filter, memo);
}
return isMemoLoaded();
} //Part::confirmPart
@@ -0,0 +1,92 @@
#ifndef SPECKLE_RECORD_ELEMENT_PART
#define SPECKLE_RECORD_ELEMENT_PART
#include <memory>
#include <optional>
namespace speckle::record::element {
class Memo;
/*!
Interface for an element part, i.e. some component in an assembly element
Although this concept is slanted toward Archicad's memo structure, the fundamentals are applicable to any data structures that separate out
the constituent parts of an assembly to support lazy loading
Note that this class is intended to be an interface - management and/or storage of the data should be provided by subclassing
*/
class Part {
public:
///Filter bits for memo loading
using filter_bits = uint64_t;
// MARK: - Types
///Unique pointer
using Unique = std::unique_ptr<Part>;
///Shared pointer
using Shared = std::shared_ptr<Part>;
///Optional
using Option = std::optional<Part>;
// MARK: - Constructors
/*!
Destructor
*/
virtual ~Part() {}
// MARK: - Functions (const)
// MARK: - Functions (mutating)
protected:
/*!
Determine if the element memo data has been successfully loaded and validated
@return True if the element memo data is loaded
*/
bool isMemoLoaded() const { return m_isLoaded && isPartValid(); }
/*!
Determine if the element memo content has been validated (elements must override according to requirements)
@return True if the element memo content is valid
*/
virtual bool isPartValid() const = 0;
/*!
Confirm that the element part data is loaded and valid (elements must override according to requirements)
@param filter Filter bits specifying memo requirements
@return True if the part data is ready to use
*/
virtual bool confirmPart(filter_bits filter) const;
/*!
Load the element memo structure (elements must override according to requirements)
@param filter Filter bits specifying memo requirements
*/
virtual void loadMemo(filter_bits filter, std::unique_ptr<Memo>& memo) const = 0;
/*!
Set whether the element memo data has been loaded (does not establish validity, but prevents multiple attempts to reload)
@param state True if the element memo data is loaded
*/
void setMemoLoaded(bool state) const { m_isLoaded = state; }
/*!
Send the element part back to a memo structure for storage (elements must override according to requirements)
@param memo The memo to carry the data
@return True if the data was successfully sent
*/
virtual bool send(Memo* memo) const = 0;
/*!
Receive the element memo data from a memo structure (elements must override according to requirements)
@param memo The memo carrying the data
@return True if the data was successfully received
*/
virtual bool receive(const Memo& memo) const = 0;
private:
///True if the memo data has been loaded into the element (mutable to support lazy loading)
mutable bool m_isLoaded = false;
};
}
#endif //SPECKLE_RECORD_ELEMENT_PART
@@ -0,0 +1,158 @@
#include "Speckle/Record/Element/Interface/SegmentedColumn.h"
#include "Speckle/Record/Element/ColumnSegment.h"
#include "Speckle/Record/Element/Memo.h"
#include "Speckle/Record/Element/Interface/Assembly/Path.h"
#include "Speckle/Utility/BIMMemory.h"
using namespace active::serialise;
using namespace speckle::record::attribute;
using namespace speckle::record::element;
using namespace speckle::utility;
namespace speckle::record::element {
class SegmentedColumn::Data {
public:
friend class SegmentedColumn;
std::vector<ColumnSegment> segments;
};
}
/*--------------------------------------------------------------------
Default constructor
--------------------------------------------------------------------*/
SegmentedColumn::SegmentedColumn() {
} //SegmentedColumn::SegmentedColumn
/*--------------------------------------------------------------------
Copy constructor
source: The object to copy
--------------------------------------------------------------------*/
SegmentedColumn::SegmentedColumn(const SegmentedColumn& source) : base{ source } {
m_data = source.m_data ? std::make_unique<Data>(*m_data) : nullptr;
} //SegmentedColumn::SegmentedColumn
/*--------------------------------------------------------------------
Destructor
--------------------------------------------------------------------*/
SegmentedColumn::~SegmentedColumn() {}
/*--------------------------------------------------------------------
Get the number of segments
return. The number of segments (0 on error)
--------------------------------------------------------------------*/
size_t SegmentedColumn::getSegmentCount() const {
confirmPart(getPartFilter());
return m_data ? m_data->segments.size() : 0;
} //SegmentedColumn::getSegmentCount
/*--------------------------------------------------------------------
Get a column segment
index: The index of the required segment
return: The requested segment, nullptr on error
--------------------------------------------------------------------*/
ColumnSegment* SegmentedColumn::getSegment(size_t index) const {
confirmPart(getPartFilter());
return (m_data && (index < m_data->segments.size())) ? &m_data->segments[index] : nullptr;
} //SegmentedColumn::getSegment
/*--------------------------------------------------------------------
Return the bits for the part filter required to load the data necessary to build this object
return: The required filter bits
--------------------------------------------------------------------*/
Part::filter_bits SegmentedColumn::getPartFilter() const {
return APIMemoMask_ColumnSegment | APIMemoMask_AssemblySegmentCut | APIMemoMask_AssemblySegmentScheme | APIMemoMask_AssemblySegmentProfile;
} //SegmentedColumn::getPartFilter
/*--------------------------------------------------------------------
Determine if the element memo content has been validated (elements must override according to requirements)
return: True if the element memo content is valid
--------------------------------------------------------------------*/
bool SegmentedColumn::isPartValid() const {
return m_data && !m_data->segments.empty();
} //SegmentedColumn::isPartValid
/*--------------------------------------------------------------------
Load the element memo structure (elements must override according to requirements)
filter: Filter bits specifying memo requirements
--------------------------------------------------------------------*/
void SegmentedColumn::loadMemo(filter_bits filter, std::unique_ptr<Memo>& memo) const {
} //SegmentedColumn::loadMemo
/*--------------------------------------------------------------------
Send the element part back to a memo structure for storage (elements must override according to requirements)
memo: The memo to carry the data
return: True if the data was successfully sent
--------------------------------------------------------------------*/
bool SegmentedColumn::send(Memo* memo) const {
//TODO: Complete when required
return false;
} //SegmentedColumn::send
/*--------------------------------------------------------------------
Receive the element memo data from a memo structure (elements must override according to requirements)
memo: The memo carrying the data
return: True if the data was successfully received
--------------------------------------------------------------------*/
bool SegmentedColumn::receive(const Memo& memo) const {
#ifdef ARCHICAD
if (!memo || (memo.root()->columnSegments == nullptr))
return false;
if (m_data)
m_data->segments.clear();
else
m_data = std::make_unique<Data>();
//Confirm that required data is present in the memo
auto segmentPtr = memo.root()->columnSegments;
auto cutPtr = memo.root()->assemblySegmentCuts;
auto schemePtr = memo.root()->assemblySegmentSchemes;
auto profilePtr = memo.root()->assemblySegmentProfiles;
if ((segmentPtr == nullptr) || (cutPtr == nullptr) || (schemePtr == nullptr))
return false;
//Determine available item count
auto segmentCount = BIMMemory::getPtrSize(segmentPtr) / sizeof(API_ColumnSegmentType);
auto cutCount = BIMMemory::getPtrSize(cutPtr) / sizeof(API_AssemblySegmentCutData);
auto schemeCount = BIMMemory::getPtrSize(schemePtr) / sizeof(API_AssemblySegmentSchemeData);
auto profileCount = BIMMemory::getPtrSize(profilePtr) / sizeof(API_AssemblySegmentProfileData);
if ((segmentCount == 0) || (cutCount != (segmentCount + 1)) || (schemeCount != segmentCount))
return false;
auto path = dynamic_cast<const assembly::Path*>(this);
for (size_t n = 0 ; n < segmentCount; ++n) {
const API_AssemblySegmentProfileData* thisProfile = nullptr;
for (size_t i = 0; i < profileCount; ++i) {
if (profilePtr[i].segmentIndex == n) {
thisProfile = profilePtr + i;
break;
}
}
m_data->segments.emplace_back(ColumnSegment{segmentPtr[n], getTableID(), cutPtr[n], cutPtr[n + 1], schemePtr[n], thisProfile});
m_data->segments.back().setPath(path);
}
setMemoLoaded(true);
#endif
return true;
} //SegmentedColumn::receive
@@ -0,0 +1,103 @@
#ifndef SPECKLE_RECORD_ELEMENT_SEGMENTED_COLUMN
#define SPECKLE_RECORD_ELEMENT_SEGMENTED_COLUMN
#include "Speckle/Database/Identity/BIMRecordID.h"
#include "Speckle/Record/Element/Interface/Part.h"
namespace speckle::record::element {
class ColumnSegment;
/*!
Interface for a column type that is made up of consecutive segments
Note that the child segments use lazy loading to avoid high overheads when accessing data relevant to the parent column only
*/
class SegmentedColumn : public Part {
public:
// MARK: - Types
using base = Part;
///Unique pointer
using Unique = std::unique_ptr<SegmentedColumn>;
///Shared pointer
using Shared = std::shared_ptr<SegmentedColumn>;
///Optional
using Option = std::optional<SegmentedColumn>;
// MARK: - Constructors
/*!
Default constructor
*/
SegmentedColumn();
/*!
Copy constructor
@param source The object to copy
*/
SegmentedColumn(const SegmentedColumn& source);
/*!
Destructor
*/
~SegmentedColumn();
// MARK: - Functions (const)
/*!
Get the BIM application parent table ID
@return The BIM table ID
*/
virtual database::BIMRecordID getTableID() const = 0;
/*!
Get the number of segments
@return The number of segments (0 on error)
*/
size_t getSegmentCount() const;
/*!
Get a column segment
@param index The index of the required segment
@return The requested segment, nullptr on error
*/
ColumnSegment* getSegment(size_t index) const;
// MARK: - Functions (mutating)
protected:
/*!
Return the bits for the part filter required to load the data necessary to build this object
@return The required filter bits */
filter_bits getPartFilter() const;
/*!
Determine if the element part content has been validated (elements must override according to requirements)
@return True if the element part content is valid
*/
bool isPartValid() const override;
/*!
Load the element memo structure (elements must override according to requirements)
@param filter Filter bits specifying memo requirements
*/
void loadMemo(filter_bits filter, std::unique_ptr<Memo>& memo) const override;
/*!
Send the element part back to a memo structure for storage (elements must override according to requirements)
@param memo The memo to carry the data
@return True if the data was successfully sent
*/
bool send(Memo* memo) const override;
/*!
Receive the element memo data from a memo structure (elements must override according to requirements)
@param memo The memo carrying the data
@return True if the data was successfully received
*/
bool receive(const Memo& memo) const override;
private:
class Data;
///The segment data - mutable to support lazy loading
mutable std::unique_ptr<Data> m_data;
};
}
#endif //SPECKLE_RECORD_ELEMENT_SEGMENTED_COLUMN
@@ -0,0 +1,54 @@
#include "Speckle/Record/Element/Memo.h"
#include "Active/Utility/Memory.h"
#ifdef ARCHICAD
#include <ACAPinc.h>
#endif
using namespace speckle::record::element;
namespace speckle::record::element {
API_Elem_Head Memo::m_dummy{};
}
#ifdef ARCHICAD
/*--------------------------------------------------------------------
Default constructor
memo: A memo structure - this object will take ownership of anything passed to the constructor
--------------------------------------------------------------------*/
Memo::Memo(std::unique_ptr<API_ElementMemo> memo) : m_data{std::move(memo)} {
if (!memo) {
//If no data was supplied, we still allocate an empty in the event that a new element is being constructed
m_data = std::make_unique<API_ElementMemo>();
active::utility::Memory::erase(*m_data);
}
} //Memo::Memo
#endif
/*--------------------------------------------------------------------
Default constructor
--------------------------------------------------------------------*/
Memo::~Memo() {
#ifdef ARCHICAD
if (m_data)
ACAPI_DisposeElemMemoHdls(m_data.get());
#endif
} //Memo::Memo
/*--------------------------------------------------------------------
Conversion operator
return: True if the memo contains data
--------------------------------------------------------------------*/
Memo::operator bool() const {
#ifdef ARCHICAD
return m_data.operator bool();
#else
return false;
#endif
} //Memo::operator bool
+92
View File
@@ -0,0 +1,92 @@
#ifndef SPECKLE_RECORD_ELEMENT_MEMO
#define SPECKLE_RECORD_ELEMENT_MEMO
#include "Speckle/Record/Element/Element.h"
#include <memory>
#include <optional>
#ifdef ARCHICAD
struct API_ElementMemo;
#endif
namespace speckle::record::element {
/*!
Wrapper for Archicad memo data structure
The main purpose for this wrapper is memory safely. In addition to maneging ownership of the memo structure, each handle/pointer allocation
within the structure must be released to prevent leaks. This wrapper will ensure these calls are made
*/
class Memo : public Element {
public:
// MARK: - Types
///Unique pointer
using Unique = std::unique_ptr<Memo>;
///Shared pointer
using Shared = std::shared_ptr<Memo>;
///Optional
using Option = std::optional<Memo>;
// MARK: - Constructors
#ifdef ARCHICAD
/*!
Default constructor
@param memo A memo structure - this object will take ownership of anything passed to the constructor
*/
Memo(std::unique_ptr<API_ElementMemo> memo = nullptr);
#endif
/*!
Destructor
*/
virtual ~Memo();
// MARK: - Operators
/*!
Conversion operator
@return True if the memo contains data
*/
operator bool() const;
// MARK: - Functions (const)
#ifdef ARCHICAD
/*!
Get the memo root data
@return The memo root data (nullptr on failure)
*/
API_ElementMemo* root() const { return m_data.get(); }
#endif
// MARK: - Functions (mutating)
#ifdef ARCHICAD
/*!
Get the memo root data
@return The memo root data (nullptr on failure)
*/
Memo& set(std::unique_ptr<API_ElementMemo> memo) {
m_data = std::move(memo);
return *this;
}
#endif
private:
#ifdef ARCHICAD
//NB: The following is functionally redundant for memos - requirement of base class
static API_Elem_Head m_dummy;
virtual const API_Elem_Head& getHead() const { return m_dummy; }
virtual API_Elem_Head& getHead() { return m_dummy; }
///The memo data
std::unique_ptr<API_ElementMemo> m_data;
#endif
};
}
#endif //SPECKLE_RECORD_ELEMENT_MEMO

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