Speckle sharp SDK v3 guide & documentation

Hi all,

We have been developing a .NET application that deconstructs 3D models from our self-hosted deployment of Speckle.
The application has been working very well and we can’t wait to see what v3 of the Speckle connectors will offer.

We would like to investigate moving to the v3 of the Speckle SDK but, as far as I see, the guide is not up to date.

Is there a place where we can find examples or guides about the new SDK?

For us, understanding how to create an Account/Client and how to receive a Base object would be the best starting point (basically the Introduction in the .NET SDK guide).

Keep up the great work!

You are correct we have zero documentatio about next gen at this time. This is, in part, because it has been evolving faster than anyone would have time to do so.

We will be reaching stable next month, at which time we’ll begin detail multiple aspects.

Where would you like to start - pure SDK reference documentation or SDK how to’s

1 Like

That makes sense, SDK how to’s would be a great place to start.
What you have done for the v2 SDK in the guide, for example.

1 Like

Also looking forward to this :slight_smile:

Hi @Davide, @jonathon,
Are there already some examples / docs available? I would also be very interested in some basic examples for sending / receiving / (de)serialization, especially for taking our Grasshopper tooling along with Speckle v3.

Hi @jonathon, @Rob,

I have looked into receiving and de-serializing objects sent from Rhino using the .NET Speckle v3 SDK.

I’ve managed to read some basic information from the Rhino blocks in the model:

  • block definition name
  • count of block definition instances
  • block geometry volume

Following is a simplified implementation:

private async Task<Dictionary<string, (string Name, int Count, double? Volume)>> DeconstructObjectAsync(SpeckleStreamId streamId, SpeckleObjectId objectId, CancellationToken cancellationToken)
{
    var result = new Dictionary<string, (string Name, int Count, double? Volume)>();
    using var transport = _serverTransportFactory.Create(_account, streamId);

    // Read object
    var data = await _operations.Receive(
        objectId,
        transport,
        cancellationToken: cancellationToken
    );

    // Flatten instances tree
    var allObjects = data.Flatten().ToList();

    // Get all instance definitions
    var definitions = allObjects
        .OfType<InstanceDefinitionProxy>()
        .ToList();

    // Get all instance proxies
    var instances = allObjects
        .OfType<InstanceProxy>()
        .ToList();

    // Get name, volume and count
    foreach (var instanceGroup in instances.GroupBy(i => i.definitionId))
    {
        var definitionId = instanceGroup.Key;

        // Find the corresponding definition
        var definition = definitions.FirstOrDefault(d => d.applicationId == definitionId);

        if (definition is null)
        {
            continue;
        }

        double? volume = null;

        // The definition.objects contains the IDs of the geometry objects
        foreach (var objId in definition.objects)
        {
            var geometryObj = allObjects.FirstOrDefault(obj =>
                obj.applicationId == objId ||
                obj.id == objId
            );

            var members = geometryObj?.GetMembers();

            // The geometry objects contain a display value field which is a list of Base objects
            var displayValue = members?.FirstOrDefault(m => m.Key == "displayValue").Value;

            if (displayValue is not List<object> elements)
            {
                continue;
            }

            foreach (var element in elements)
            {
                if (element is not Base b)
                {
                    continue;
                }

                var baseMembers = b.GetMembers();

                // The display value Base objects contain a volume field
                if (baseMembers.FirstOrDefault(bm => bm.Key == "volume").Value is double volumeValue)
                {
                    volume = (volume ?? 0.0) + volumeValue;
                }
            }
        }

        var name = definition.name;

        result.Add(definitionId, (name, instanceGroup.Count(), volume));
    }

    return result;
}

Seems like navigating the tree has changed significantly, now the applicationId is used for linking the parent with the child object. Is this true?

What is the displayValue field, how come is the volume stored in there instead of the parent “geometry” object?

I will try to expand this with other models (without blocks, from other sources than Rhino, etc…).
Please let me know if you have suggestions for a simplified general approach, maybe using the TraversalFunc() instead?

I would also like to read the area field like we do for volume above, but that seems to be missing from the displayValue objects, at least from the model I am currently testing this on. Do you have any hints on where that could be stored?

I can provide the v2 implementation for comparison if needed.

1 Like

An object can be in a group, a block, on a layer and have a material, all of which may vary from other objects on the same layer/block/group

applicationId is used to link proxies with objects rather than a straightforward parent<>child relationship.

Hi @Davide,

Thanks for your reply, it’s appreciated!
Code seems to make sense and it’s interesting to have a first glimpse of the direction it seems to be taking.

However, we have for now decided to wait for proper documentation from Speckle. It feels like we would be doing a lot of additional work trying to figure out the exact inner workings without documentation, possibly with the risk of some major changes being implemented later as well. As the same seems to apply for specklepy (also no real documentation available as of yet?), we believe it’s better to wait for the moment that Speckle does release a stable version with proper docs.

2 Likes