SUEntityGetAttributeDictionary function does not work with SUGeometryInputRef

I am pretty much beginner in Sketchup programming. Working on generating a custom building model programmatically using C API. To generate wall faces with several opening on it I am using “SUGeometryInputAddFace” method as below

size_t outerLoopIndex;
result = SUGeometryInputAddFace(input, &loop, &outerLoopIndex);

That is working fine and I also need to attache attribute dictionary with the faces. It seems SUEntityGetAttributeDictionary does not work with SUGeometryInputRef. Is there a work around? Is there any function that convert the SUGeometryInputRef to SUEntityRef?

I am trying the following code that does not work.

SUResult result = SU_ERROR_NONE;
SUAttributeDictionaryRef dictionary = SU_INVALID;
Result = SUEntityGetAttributeDictionary(input, "mydictionary", dictionary);

I will highly appreciate any suggestion, workaround or example code.

SUGeometryInputRef is a virtual geometry build context. The objects created using it do not become SUEntityRef or SUDrawingElementRef subclass objects until after they have been added to the model.

@tt_su said in the open issue (linked below) …

There isn’t any way to add attributes via SUGeometryInput in the existing API.
I’m logging this as a feature request.

No. Because SUGeometryInput is a virtual “helper” object and not a subclass of SUEntityRef.

Yes, but it might be slower. In your case a few building walls you will not notice any slowness.

Basically the SUGeometryInput is meant for very large meshes and big complex geometry additions.

EDIT: Corrected by Thomas below!
You can create and add SUFaceRef individually to SUEntitiesRef collections. (Either the model’s OR a definition’s entities collection.) Use SUFaceAddInnerLoop to add the opening(s).

Since a SUFaceRef is a subclass of SUEntityRef you can upcast it and use the superclass’ attribute dictionary functions.

SUFaceCreate and SUFaceCreateSimple should not be used - it doesn’t merge vertices, which is a requirement of a well formed SketchUp model. If for instance you try to create a cube with SUFaceCreate you’ll have overlapping vertices and edge. (There is a Bug warning in the docs for both of these functions.)

This means it’s hard to assign properties to created geometry via the C API unless SUGeometryInputRef doesn’t support it.

You might be able to apply the same workaround that is some times done in the Ruby API with Geom::PolygonMesh; if the input order of the faces to SUGeometryInputRef matches the iteration order of SUEntitiesGetFaces. Note that it’s a fragile workaround as it’s implementation detail. But until we have better API for this it might be your best (only) choice.

1 Like

Ah… sorry I missed that warning.

Sounds top me like this should be high up on the priority list.

Dan/Thomas, Thank you very much to both of you for replying my question. I could able to get an work around the issue. It is in the same line what you had mentioned. Here is the code snippet that I used.

//SUGeometryInputRef geominput = SU_INVALID;
// Geometry creation code goes here
// Add the face to the entities collection
result = SUEntitiesFill(localEntities, geominput, true);

// Get all the faces from the entities object
SUAttributeDictionaryRef dictionary = SU_INVALID;
size_t faceCount = 0;
SUEntitiesGetNumFaces(localEntities, &faceCount);
if (faceCount > 0) {
    std::vector<SUFaceRef> faces(faceCount);
    SUEntitiesGetFaces(localEntities, faceCount, &faces[0], &faceCount);
    // Get the last created face and convert to an entity.
    SUEntityRef entity = SUFaceToEntity(faces[faceCount - 1]);
    result = SUEntityGetAttributeDictionary(entity, "mydictionary", &dictionary);
    SUTypedValueRef myvalue = SU_INVALID;
    result = SUTypedValueCreate(&myvalue);
    result = SUTypedValueSetString(myvalue, tagname.c_str());
    result = SUAttributeDictionarySetValue(dictionary, "plane_type_override", myvalue);
    result = SUTypedValueRelease(&myvalue);

// Release the geometry input
result = SUGeometryInputRelease(&geominput);

I could not use SUFaceCreateSimple for creating face because I need to put the holes into the face. Not found any better way to do that. Well, this one is kind a crude way and I am sure there could be a much better way to assign Attribute to face. However, I still believe including a overloaded method below in the API could make life much easier.

SU_RESULT SUEntityGetAttributeDictionary(SUGeometryInputRef geominput, const char* key, SUAttributeDictionaryRef *dictionary);

I will really appreciate if you could please recommend me any book or tutorial with lots of example code, particularly for using C API. I am a total beginner in the SketchUp application.

Thanks again for all your help.

Yes well after an unpierced face is added to an entities object, holes can be added using the SUFaceAddInnerLoop function.

But, as Thomas said it would be problematic even if you did as the edges and vertices would not be merged.

Sorry, but I do not think that there is “lots of example code” in a book for the C API.

Historically speaking, the C API is rather young compared to the other APIs and the SketchUp application itself.

The largest collection of C API examples is distributed with the C SDK in it’s "samples" directory.

Other than this there are snippets here in the forum topic threads.

It is C. C compilers do not support overloading well (if at all.)

Also the entire SketchUp C API is written so that you must cast your subclass entity to a SUEntityRef in order to use the dictionary methods.

As I said above … SUGeometryInputRef is not a subclass of SUEntityRef (in the C++ core of SketchUp.) So it itself cannot be upcast to SUEntityRef.

Further, as mentioned SUGeometryInputRef is not an entity-like object, it’s a collection object. It is kind of like a temporary group that you add new geometry to, and when done it is merged with a SUEntitiesRef collection object.

So, what needs to happen is SUGeometryInputRef needs to get it’s own “wrapper” functions that allow attachment and modification of dictionaries to the “virtual” geometric objects within (ie, edges, faces, vertices, arcs, curves, etc.)

But those objects are not yet SUEntityRef subclass objects, so they must be accessed by index.

So the functions would need to be something like:

SU_RESULT SUGeometryInputSetAttribute (
  SUGeometryInputRef geom_input, SURefType object_type, size_t index,
  const char *dict_name, const char* key, SUTypedValueRef value
// Perhaps passing NULL as the value could delete the attribute ?

SU_RESULT SUGeometryInputGetAttribute (
  SUGeometryInputRef geom_input, SURefType object_type, size_t index,
  const char *dict_name, const char* key, SUTypedValueRef *value

SU_RESULT SUGeometryInputGetNumAttributes (
  SUGeometryInputRef geom_input, SURefType object_type, size_t index,
  const char *dict_name, const char* key, size_t *count

SU_RESULT SUGeometryInputGetAttributeKeys (
  SUGeometryInputRef geom_input, SURefType object_type, size_t index,
  size_t len, const char* keys[], size_t *count

We’ve been talking within the team about investing more time in examples etc. Meanwhile, feel free to ask here on the forum and we’ll try our best to respond.

1 Like