Grasshopper Workflow - Which Libraries / Modules to use?

Note: This is mainly a Grasshopper question, but there is no category “compas_ghpython”. And as a new user I cannot put in the necessary links and images. I will post them below this first post.

My struggle
I’m trying to understand the best way to implement COMPAS on my grasshopper/rhino workflow that is explained in the image. I’m struggling to understand the principles of COMPAS and how best to use it. I’ve already watched the OSArch intro by Gonzalo on Youtube. And I’ve been reading through the tutorials + API reference. I’m still struggling though.

The Context:
Within Grasshopper: I especially need to convert GH (Grasshopper) and RH (Rhino referenced) geometry INTO compas geometry, but I cannot find the way to do this within GH. As I understand from the gh_python docs, the “artists” and “utilities” convert compas geometry to GH (artists) or Rhino (utilities). All I need to do is (#s are aligned to the image):

  1. Convert Rhino referenced geometry (ex. it has a Rhino GUID) to COMPAS geometry. Question: what modules to use?

  2. Convert Grasshopper referenced geometry (ex. only a geometry in the GH interface with no link to Rhino or a GUID) to COMPAS geometry. Question: what modules to use?

  3. Send this geometry to be processed in a remote procedure, either locally or cloud. As I understand I can use the RPC Module, but I haven’t yet tested. No questions here yet.

  4. Bring back both the COMPAS geometry + new custom attributes on that geometry into GH. Where do we store the custom attributes on the geometry class? Question: Can we store custom attributes on the self.data or will that conflict with the “self.to_data” or “self.to_json” methods?

  5. Question: What is the correct methods to use to deserialize both the COMPAS geometry + custom attributes into one object to send to a DB or full JSON file?

  6. General Question: Do you have anywhere the geometry schemas for all the COMPAS objects, especially geometry and datastructures? This is an example that applies to the GeoJSON geometry types. I want to have a set of schemas when serializing and deserializing geometry. I cannot find it in the documentation or the API reference.

Thank you for answering all these long questions.


Here is an example of the code that works for me. But this only works in GH to convert a COMPAS point to the compas_ghpython “Artist”

from compas.geometry import Point
from compas_ghpython import artists
from compas_ghpython import utilities

# GH output of artists
output_artists = []
# to generate unique X, Y values for points
values = range(0,range_limit)

# artists will convert COMPAS geometry to the software
for v in values:
    p = Point(v,v,0)
    output_artists.append(artists.PointArtist(p).draw())

In this next example, the compas_rhino modules don’t work in the GH environment to convert a rhino geometry to a COMPAS geometry. Question: what should we use in Grasshopper to achieve this functionality?

# Inputs
# rh_polylines - type hint == guid
# rh_points - type hint == point3d

# Output Artists
polylines = []
points = []

for guid in rh_polylines:
    rh_pline = RhinoCurve.from_guid(guid)
    c_pline = rh_pline.to_compas()
    print type(c_pline)
    
for point in gh_points:
    p = RhinoPoint.from_geometry(point)
    points.append(PointArtist(p).draw())

Supplement to the above post.

Image 1 for the code block 1

Supplement to the above post.

Image 2 for the code block 2

I can comment from my experience:

  1. Convert Rhino referenced geometry (ex. it has a Rhino GUID) to COMPAS geometry. Question : what modules to use? 2. Convert Grasshopper referenced geometry (ex. only a geometry in the GH interface with no link to Rhino or a GUID) to COMPAS geometry. Question : what modules to use?

I would use compas_rhino.geometry as you already have. Not sure there is something like compas_ghpython.geometry implemented yet, so I would stick to the former. It has work for me with both geometry imported from Rhino or geom created in GH. Probably the issue stems from the data type in the ghpython component on the gh canvas. If you right click on the rhino geometry input, you could try setting that to RhinoGeometry or keep the GUID representation of it. See how that plays out.

  1. Bring back both the COMPAS geometry + new custom attributes on that geometry into GH. Where do we store the custom attributes on the geometry class? Question : Can we store custom attributes on the self.data or will that conflict with the “self.to_data” or “self.to_json” methods? Question : What is the correct methods to use to deserialize both the COMPAS geometry + custom attributes into one object to send to a DB or full JSON file?

Like any other Python object, you are free to modify any of its attributes to your liking. However, problems may arise with the serialization mechanism if the changes we make are not compatible with it as it stands. If you need some custom behavior, you could override the to_data() from_data(), which largely controls the serialization dynamics. One rule of thumb (AFAIR) is that only native Python objects like lists, dictionaries, integers, etc. can be serialized. Setting e.g. a Rhino geometry object of an attribute may not be serialized explicitly.

Hope this helps.

1 Like

@arpastrana Thank you for looking at the code!

Note: I’m using COMPAS 1.1.0

  1. I have tested what you suggest for the geometry. I used the “ghdoc” object input so that the component creates a GUID. All the other 'from_geometry" and “from_selection” and “from_object” didn’t work for me. Both the Rhino and Grasshopper files need to be downloaded from Wetransfer

Note: The POINT functionality works no problem.
Note: The POLYLINE functionality does NOT work. There is an error in the RhinoCurve.to_compas() that breaks the code. I cannot tell why.

  1. The to_data() and to_json() will not work with custom attributes as far as I can tell. It works fine for decoding/encoding the geometry, but nothing more. I really hope that the COMPAS team responds. Otherwise, we will override the function.
__author__ = "Darrel Ronald"
__version__ = "2021.04.09"


import compas
import compas_ghpython.utilities
#import compas_ghpython.artists
from compas_rhino.geometry import * #BaseRhinoGeometry, RhinoCurve, RhinoPoint
from compas_rhino.artists import *
from compas_rhino.utilities import *
from compas.geometry import *
from compas.datastructures import *
from compas.utilities import *

# Output Artists
points = []
polylines = []



# INPUT == gh_points
# Convert Grasshopper Referenced Geometry
# 1. Convert to compas_rhino with compas_rhino.geometry
# 2. Convert to compas geometry with compas.geometry
# 3. Convert to rhino geometry with compas_rhino.artists

for i in gh_points:
    print "type i in gh_points";  print type(i)
    # Create COMPAS RhinoPoint wrapper from GUID
    rh_point = RhinoPoint.from_guid(i)
    
    # Cast RhinoPoint to COMPAS.geometry.Point
    c_point = rh_point.to_compas()
    print "type: c_point:"; print type(c_point)
    
    # Create COMPAS artists to return to Rhino Geometry
    # PointArtist returns a LIST for some reason here
    artist = PointArtist(c_point).draw()
    print "artist:"; print type(artist)
    for a in artist:
        points.append(a)

for point in points:
    print "item in points list:"; print type(point)




# INPUT == rh_polylines
# Convert Rhino Referenced Geometry
# 1. Convert to compas_rhino with compas_rhino.geometry
# 2. Convert to compas geometry with compas.geometry
# 3. Convert to rhino geometry with compas_rhino.artists

for i in rh_polylines:
    print "type i in polylines:"; print type(i)
    
    # compas rhino wrapper
    rh_pline = RhinoCurve.from_guid(i)
    print "type rh_pline:"; print type(rh_pline)
    
    c_pline = rh_pline.to_compas()
    print "c_pline"; print type(c_pline)
    
    # compas geometry
    if rh_pline.is_polyline():
        isinstance(c_pline, comas.geometry.Polyline)
        print "it is a polyline"
    elif rh_pline.is_line():
        isinstance(c_pline, compas.geometry.Line)
        print "it is a line"
        
    
    # rhino artist
    artist = PolylineArtist(c_pline).draw()
    print "pline artist:"; print type(artist)
    for a in artist:
        polylines.append(a)
    
    #polylines.append(CurveArtist(c_pline).draw())
    #CurveArtist(c_pline).draw()



"""
Note Darrel: This is the COMPAS example of casting a RhinoCurve to COMPAS Curve. I follow it but it doesn't work for me.


Wrapper for Rhino curve objects.

Parameters
----------`Preformatted text`
None

Attributes
----------
start (read-only) : Rhino.Geometry.Point3d
    The start point of the curve.
end (read-only) : Rhino.Geometry.Point3d
    The end point of the curve.
points (read-only) : list of RhinoGeometry.Point3d
    List of points between start and end, defining the geometry of the curve.

Examples
--------
>>> rhinocurve = RhinoCurve.from_guid(guid)
>>> curve = rhinocurve.to_compas()
>>> if rhinocurve.is_line():
...     isinstance(curve, compas.geometry.Line)
...
True
>>> if rhinocurve.is_polyline():
...     isinstance(curve, compas.geometry.Polyline)
...
True
"""

To better understand what is going on, it would be helpful if you could share what error you get when using RhinoCurve.to_compas() and what are the custom attributes you want to serialize.

But you are right, the moment you want to add something new to a COMPAS object, you will (most likely) have to override the encoding/decoding methods. I don’t see an easy way around that.

Thanks @arpastrana

Here is the code. Below an image with Line #s.

Runtime error (ValueErrorException): unable to convert 00000000-0000-0000-0000-000000000000 into Curve geometry

Traceback:
  line 1002, in coercecurve, "C:\Users\Darrel\AppData\Roaming\McNeel\Rhinoceros\6.0\Plug-ins\IronPython (814d908a-e25c-493d-97e9-ee3861957f49)\settings\lib\rhinoscript\utility.py"
  line 2063, in CurvePoints, "C:\Users\Darrel\AppData\Roaming\McNeel\Rhinoceros\6.0\Plug-ins\IronPython (814d908a-e25c-493d-97e9-ee3861957f49)\settings\lib\rhinoscript\curve.py"
  line 63, in points, "C:\Users\Darrel\AppData\Roaming\McNeel\Rhinoceros\6.0\scripts\compas_rhino\geometry\curve.py"
  line 101, in to_compas, "C:\Users\Darrel\AppData\Roaming\McNeel\Rhinoceros\6.0\scripts\compas_rhino\geometry\curve.py"
  line 62, in script

I was debugging in deeper with my colleague. We think the issue is related to the lack of GUID created by the COMPAS “wrapper” RhinoCurve. It doesn’t really contain a GUID so it cannot convert the wrapped RhinoCurve to a COMPAS curve.

there is currently no NURBS geometry in base COMPAS so converting Rhino NURBS curves and/or surfaces to COMPAS equivalents will not work. only line, polyline and circle are supported…

COMPAS geometry objects also do not support any form of data attributes. the serialisation mechanism for geometry objects is only there to allow those objects to be sent over a network or to save them to disk for later use. if you want to store data, i suggest you use a data structure instead…

also, i don’t really understand why you have to rereference geometry in the first place. if you created the geometry you know where it is. after all you put it there. so why do you need to “find” it again? the GH output is just visualisation of something you created…

I’m sure that you’ve researched implementing NURBS in COMPAS. Both Rhino NURBS (I assume we could use the Rhino3DM Python library) and Verb NURBS (JS but can be compiled to Python and other languages) are open source options.

What is the best way to represent curvature of lines? Will COMPAS automatically split a curved Polyline into segments of ARCs and LINEs?

Can you explain if it is in the roadmap? And the decisions for or against implementing? I would have thought that it is critical for the structural, robotic and digital fabrication teams using COMPAS at ETHZ.

Thanks.

Yes, this must seem weird. But if you look at the first (TOP) image, I want to send the geometry to an outside python process, then bring it back into Rhino. This means that I need to serialize the Geometry to COMPAS geometry, then bring it back into RHINO and deserialize it to Rhino Geometry. So I am testing the process (just missing the sending of the process to the background RPC).

There might have been a NURBS package for COMPAS in the making at some point?

However, I do agree with the current COMPAS approach to geometry which centers around discrete geometry such as meshes and polylines instead of continuous geometry (surfaces and curves). A mesh with an infinitely large number of subdivisions will tend towards a surface.

Besides the abundant work and useful algorithms stemming from the computer graphics community that only work in a discrete setting, if we think about it, most of the architectural structures we build are very well represented with discrete geometry. For example, the elements of a cable net, the beams in a reticulated shell, the walls and roofs of a multistorey building, etc. This is not to say that curves are not possible with discrete geometry --check all the great work done in computer graphics --, it is just that those objects that look curvy in a discrete setting on our screen are actually discretized (again, like meshes and polylines) up to some small resolution.

@arpastrana
Thanks for your reaction! Sorry for the late reply.

I’m really not sure what the roadmap is for COMPAS as concerns more continuous curve types supported by Rhino. I suspect they will give some input to this thread when they have more time. Tom mentioned that they’re working on a series of issues related to my other post.

I’m not a geometry specialist, but I assume you’re suggesting discrete geometry as opposed to continuous geometry in general. I think that it’s ideal to (in general) support the range of geometry that the software itself supports. This is from a “user” point-of-view. In essence, if you use BSplines, Splines, NURBS curves, Bezier curves, etc. within Rhino (or whichever software)… and you want to preserve that fidelity, than ideally it is preserved in the COMPAS data description. Perhaps it’s not always needed, I definately understand that.

My use case is that a user draws a geometry… a continous, smooth (not faceted) line… An example: a concrete edge of a road or intersection. In reality it is a smooth curve and in the 3D digital model it is a smooth curve. If that curve gets faceted, there is a loss of data for a user. This isn’t ideal.

In general, I think that today users (me included) expect higher fidelity in virtual models / 3D Geometry. And combined with that is a high expectation of transferring that data fidelity around between software / web platforms / etc. So I’m trying to meet that demand with my software plugin.

Sorry if I’m a bit late to the party, but, thanks @Maketank for the thorough and detailed description of the workflow. I find your original workflow diagram an excellent map of things that should work.

I will go through the thread in detail and reply as good as possible in the coming days.

1 Like

@gonzalocasas I recently discovered this github repository for a Python script of De Boors Algorithm to convert a nurbs curve to polyline. Perhaps if COMPAS doesn’t want to integrate Nurbs geometry, this is a way to at least treat the conversion from NURBS Curve (Rhino) to a faceted curve in COMPAS? For me the ideal would be to have full NURBS integration, but I’m not aware of how difficult this would be for you. Great work though so far!!!

We do want, it just takes time. I don’t see it mentioned in the thread, so, not sure if you’re aware, but compas_nurbs exists already, even if still in its infancy.

thanks @gonzalocasas.
Thanks for the NURBS link, that didn’t come up in the previous messages. There are two threads that I posted, this one and also another about Rhino from_geometry().

Above in this thread, there are some comments about NURBS actually, I found that when converting from Rhino to COMPAS, there are only three lined types supported which don’t capture the richness of a NURBS curve. The NURBS curve isn’t implemented yet, as you say.

Maybe I need to review the NURBS package as you mention in the meantime?
I notice that the COMPAS NURBS you sent is actually not integrated to the COMPAS Docs, that’s also why myself and others didn’t see it. It’s in the Gramazio Kohler repositories.