[SpecklePy] Commiting Geometry to a Branch

Hi Speckle Community,

Looking to add custom geometry to a branch using SpecklePy. The geometry is envisaged to be transparent spheres with a series of user information (see screenshot below).

Data I want to write with the geometry, evaluation of where offsets occur with relevant coordinates I already have. The information is basically just ready to be written to the speckle server.

Grabbing some inspiration from Block and Line examples (Examples | Speckle Docs), a commit is created, however, I can’t see any geometry when viewing online. Does anyone have some experience creating visible user-defined geometry with SpecklePy (with attributes / properties attached to it).

Thanks in advance!

Björn :slight_smile:

cc: @AlexHofbeck, @ltascheva

2 Likes

The extent of my direct generation of geometry with the SDKs is limited to hacky experiments. Still, we have heard from users committing visual artefacts from analysis - that look adjacent to what you describe.

You may describe the gap between perfectly valid Speckle Objects and what ends up as visible in the Speckle Viewer.

I’m happy to review your commits and provide feedback on what might be missing. Alternatively, you may receive these commits if you have access to Grasshopper, Blender, or another connector. In that case, the Connectors translate Speckle to Native in a way that the Viewer doesn’t. Part of the action of the Connectors on data creation Native To Speckle is the addition of the fallback meshes as displayValues.

1 Like

Hi @jonathon ,

thanks for your response!

I have sent Point objects to the server and received the object id / hash in return.

In my example, I have 8 points which I want to commit at once. The object_id parameter for client.commit.create() seems to only accept one object_id. I am thinking one needs to add the Point objects into one model object (maybe the Base class ? ) and then commit that as a single object (with the points inside). Is this the correct thinking?

Should this be the case, I don’t see where we (in the Base class) can add objects. By inspecting the dir(Base):

[‘add_chunkable_attrs’, ‘add_detachable_attrs’, ‘applicationId’, ‘get_children_count’, ‘get_dynamic_member_names’, ‘get_id’, ‘get_member_names’, ‘get_registered_type’, ‘get_serializable_attributes’, ‘get_typed_member_names’, ‘id’, ‘of_type’, ‘speckle_type’, ‘totalChildrenCount’, ‘units’, ‘update_forward_refs’, ‘validate_prop_name’].

There are a lot of get methods / attributes, however none related to adding new “children”?

Cheers!
Björn

1 Like

Correct, you can use a Base object as the commit payload.

The Base object can have as many dynamic properties as you wish, but your commit object would often use a property that is fit for your use. Our serializer and deserializer will handle any or all.

Our QGIS connector adds a layers property, as that makes semantic sense for the source information. When that is received in Revit, say, that name has no magic properties, the Connector logic will parse all the content in whatever structure.

Likewise, Navisworks Connector (using .NET) has an elements property. Grasshopper defaults to data but this can be overridden.

Optionally, this property can be prefixed with @ which indicates to specklepy that these would be detached objects.

taking you script as it exists, a slight modification:

commit_object = Base()
commit_objects["@points"] = []

for index, row in offset_df.iterrows():
    point = Point.from_coords(row["x_y"]. row["y_u", row["z_u"])
    commit_object["@points"].append(point)

obj_id = operations.send(base=commit_object , transports=[transport])

commit_id = client.commit.create(
              stream_id=stream_obj.id,
              object_id=obj_id,
              message="All the point!",
              source_application="BG_APP",
          )

You’ll see that the hashing is performed once. The @ prefix will, in turn has all those points individually, useful to address them sometimes later on.

2 Likes

Awesome! Thanks @jonathon, I will give that a shot now! :slight_smile:

1 Like

Hi @jonathon,

I made a slight modification to the above script you sent. The commit_objects["@points] threw out a key error, so I assumed I needed to add this attribute first, with the value being an empty list.

from specklepy.objects.geometry import Point, Base
from specklepy.transports.server import ServerTransport
from specklepy.api import operations

commit_object = Base()
commit_object.__setattr__("@points", []) # This seemed to be required?
commit_object["@points"]

for index, row in offset_df.iterrows():
    point = Point.from_coords(row["x_u"], row["y_u"], row["z_u"])
    commit_object["@points"].append(point)

With this, the results seem to be correct:

Without the added attributes, I get the following error message:

I understand the properties are dynamic, however, without adding the attribute, it seems to look under getitem which makes sense why I get a KeyError.

Any objections with the added line of code?

Regards,
Björn :slight_smile:

Apologies i made an edit to the code I posted above - dangers of using the community forum as my IDE :smiley:

commit_objects["@points"] = []

should be sufficient.

1 Like

ah perfect, much easier haha. Thanks!

1 Like

Hey @jonathon,

Back to the original question - geometry! :smiley:

We see little points in the commit (small success :sweat_smile:):

Referring to the first image of this thread, I want to create spheres of a certain colour. To see how a sphere could be defined ( / get some inspiration), I did the following:

  • Modelled a sphere in Rhino
  • Using the connector, exported this to the stream
  • Inspected the properties online (to try and “backtrack” the steps for defining a sphere)

The simple sphere from Rhino:

An output of it’s definitions:

I then tried defining a Box as the above export:

from specklepy.objects.geometry import Point, Base, Box, Plane, Vector
from specklepy.objects.primitive import Interval
from specklepy.transports.server import ServerTransport
from specklepy.api import operations
from specklepy.objects.units import Units
from specklepy.objects.other import DisplayStyle

commit_object = Base()
commit_object["@points"] = []

for index, row in offset_df.iterrows():
    point = Point.from_coords(row["x_u"], row["y_u"], row["z_u"])
    
    box = Box()
    
    box["basePlane"] = Plane()
    
    box["basePlane"]["origin"] = point
    box["basePlane"]["normal"] = Vector.from_coords(row["x_u"], row["y_u"], row["z_u"])
    box["basePlane"]["xdir"] = Vector.from_coords(row["x_u"], row["y_u"], row["z_u"])
    box["basePlane"]["ydir"] = Vector.from_coords(row["x_u"], row["y_u"], row["z_u"])
    
    box["xSize"] = Interval.from_list([row["x_u"], row["offset_node_x"]])
    box["ySize"] = Interval.from_list([row["y_u"], row["offset_node_y"]])
    box["zSize"] = Interval.from_list([row["y_u"], row["offset_node_y"]]) # Annahme: xSize
    
    box["displayStyle"] = DisplayStyle()
    box["displayStyle"]["color"] = -65281
    box["displayStyle"]["_units"] = Units.m
    box["displayStyle"]["lineweight "] = 0.25
  
    commit_object["@points"].append(box)

transport = ServerTransport(client = client_obj, stream_id = stream_obj.id)
obj_id = operations.send(base=commit_object , transports=[transport])
commit_id = client_obj.commit.create(stream_id = stream_obj.id, object_id = obj_id, message="Test!")

However, this approach didn’t seem to work and now I don’t see anything. Also, I don’t think I should be going this deep to create something visible in the speckle viewer online, or?

Thanks in advance!

Björn

to my earlier reply - bypassing the viewer, what do you see receiving that box commit in rhino?

For the envisaged workflow, it would be ideal to have the viewer as the central point (between Revit models and the script that evaluates potential points of column offsets). However, when trying to receive the box commit in Rhino I do get a failed message:

image

I completely understand that. I was delaying my looking at the conversions to Viewer compatible. But seeing as the conversion into Rhino and now having looked into the viewer code, the primitives supported do include Box, so I’ll take a look at your code in detail.

  SpeckleType.Pointcloud,
  SpeckleType.Brep,
  SpeckleType.Mesh,
  SpeckleType.Point,
  SpeckleType.Line,
  SpeckleType.Polyline,
  SpeckleType.Box,
  SpeckleType.Polycurve,
  SpeckleType.Curve,
  SpeckleType.Circle,
  SpeckleType.Arc,
  SpeckleType.Ellipse

Could you share a stream you are sending to?

Could we add you as a member to our current Server? :slight_smile:

cc: @AlexHofbeck

That won’t be necessary - I’ll take your code as is , run it against xyz and then make suggestions from there

2 Likes

Attached is the code as well as the export of the dataframe, which the code references :slight_smile: Thanks in advance!
extract_from_code.py (1.8 KB)
Stützenversätze.xlsx (12.1 KB)

2 Likes

off the mark, before digging too far, the excel references x_o, y_o and z_o but the code you show is x_u, y_u and z_u, just crossed wires??

There are _o as well as _u coordinates.

_o is just the abbreviation for top of the column and _u for the bottom of the column (from “oben” and “unten” in german). I always assessed moving up a column run, therefore the offsets are at the base of a column (hence x_u, y_u, and z_u).

1 Like

Update on the original post - success!

A massive thanks to @jonathon and the Speckle Team for the help and guidance! Taking the time to answer all questions, review code etc. is very much appreciated! :slight_smile:

cc: @AlexHofbeck @ltascheva

5 Likes