Error with "Component Options" Tool and Custom Attribute Dictionaries created using API

Dear Community,
I am developing a custom SketchUp plugin that generates components with custom attribute dictionaries (attribute_dictionaries) where I store complex attributes (including Vector3d, Point3d, and Array). These attributes are essential for the plugin to re-read and process components later.

The problem arises when I use SketchUp’s “Component Options” tool on these components. I receive the following error:

“ERROR: Callback function error: Unable to get property ‘length’ of undefined or null reference @ /dcbridge.js[601]”

From what I have observed so far:

  1. The dictionaries created by my plugin contain complex attributes (Vector3d, Point3d, and Array), which seem not to be supported by the “Component Options” tool, or at least are not formatted conventionally (e.g., according to JSON standards).

  1. If I remove these complex attributes from the dictionary, the tool works correctly again. However, my plugin requires these complex data to re-read and process the components later, so I cannot permanently remove them.

Here are my questions:

  1. Is there a way to exclude my custom dictionary from being read by the “Component Options” tool?
  2. What is the recommended practice for storing complex data, such as vectors, 3D points, or arrays, in a component’s attribute dictionary without interfering with SketchUp’s native functionality?
  3. When exporting to IFC, my custom dictionaries are also included, which confirms that SketchUp attempts to read them. Is there a way to “hide” or make a custom dictionary invisible to SketchUp so that it does not get exported in the IFC file?

Thank you in advance for any suggestions or advice!

Best regards,

(Answer to 1.) The Component Options dialog and the Dynamic Components extension does not use your custom dictionaries (ie, named "Rebar_creator".) The DC extension only uses its’ own attribute dictionaries and the Classification dictionaries (such as the IFC dictionaries.)

So, I am not quite sure why you are seeing this JS bridge error. This error has been reported in the past as the DC extension does have open bugs in it.

(Answer to 2.) Store your data in a dictionary that is unique to your company namespace and extension, and avoid storing into dictionaries listed in "AppliedSchemaTypes" and "dynamic_attributes". Ie, the model space is a shared space, so prefix your dictionary names with your top-level namespace module name and your extension submodule name.
Ex: If your top-level namespace (company) name is "Pedretti and the extension submodule name is "RebarCreator", then use "Pedretti_RebarCreator" for the name of the dictionaries.

(Answer to 3.) I don’t think that your dictionaries should be exporting to IFC, unless you’ve mistakenly added your custom dictionaries nested beneath the IFC Schema dictionaries or the "dynamic_attributes" dictionaries. @brewsky Jan can you verify?
An AttributeDcitionary is itself an Entity subclass which means it also can have its’ own AttributeDictionaries collection of nested child dictionaries, each of which can also have child nested dictionaries, … and so on, etc. etc.


That said, there have been some discussions in the past as to what types of objects can be stored and retrieved from attribute dictionaries. (Try a search in this category.)

1 Like

Hi @DanRathbun,

Thank you for the detailed response and suggestions. I conducted some tests to better understand the behavior of my plugin and the potential conflicts. Here are the results:

1. Verifying the dictionaries associated with the component

I ran a script to list all dictionaries associated with the problematic component. The results show that my custom dictionary Rebar_creator is not nested within dynamic_attributes or any other reserved dictionaries.
Here’s the complete list of dictionaries:

  • SU_DefinitionSet: Contains standard metadata (e.g., Price, Size, Url).
  • Rebar_creator: My custom dictionary with complex attributes (e.g., Point3d, Vector3d, Array).
  • AppliedSchemaTypes: Includes an IFC classification (IfcReinforcingBar) that is automatically create.
  • IFC 2x3: Contains information related to the IFC classification (e.g., SchemaType).

I can confirm that, although my dictionary is not accidentally nested within the IFC schema dictionary, if I export the model created with my plugin to IFC, my dictionary appears as a Pset.

2. Changing the dictionary name

I renamed the dictionary from Rebar_creator to Alessandro_Pedretti_RebarCreator to ensure it is unique and does not conflict with reserved dictionaries. However, despite this change, the issue persists: the Component Options command continues to generate the error.

3. Community research

Lastly, I searched the community for references regarding the types of data that can be stored in a dictionary, and I came across this post: Set Attribute does not support custom classes, which links to the official SketchUp documentation: AttributeDictionary#[]= instance method.

The documentation clearly defines the supported categories, including Point3d, Vector3d, and arrays, which are exactly the types I store in my dictionary. This confirms that SketchUp has no issues handling these data types in dictionaries.

In fact, through further testing on the component created by my plugin, I discovered that saving individual values of Point3d, Vector3d, or arrays of non-complex objects does not cause any conflict, and the Component Options command works without issues. However, if I save an array containing multiple Point3d or Vector3d objects, the Component Options command generates an error and stops functioning correctly.

It seems that Component Options cannot handle arrays containing multiple Vector3d or multiple Point3d.

Do you have any advice on how I might resolve this issue?

Thank you in advance for your support and any further suggestions!

This appears to be a bug in the Component Options dialog of Dynamic Components. I can reproduce by writing an attribute with a point array to a custom dictionary.

model = Sketchup.active_model
component_def = model.definitions.add
component_def.entities.add_cpoint(ORIGIN)
model.entities.add_instance(component_def, IDENTITY)
component_def.set_attribute("my_attributes", "point-array", [ORIGIN])

I’ll log a bug but Dynamic Components isn’t very likely to be patch soon as it’s an old code base with a lot old tech debt, and numerous other extensions rely on its internal functionality and risks breaking if we touch anything.

For now my only recommendation would be to try to write the attributes to the component instance or a nested entity, if possible.

3 Likes

Logged as SKEXT-4452

2 Likes

Yes, I can also reproduce on SU2024.0.2 with DC extension version: 1.8.3

:+1:

I think we can say without doubt that the extension has been broken for quite some time.

Automatically created by what? Your plugin or a manual IFC tagging by the user?

Can you show me the AppliedSchemaTypes dictionary in the Attribute Inspector?

1 Like

Hello @ene_su and @DanRathbun,

Thank you both for your suggestions!

Results

Following ene_su’s advice to associate the dictionary directly with the component instances instead of the definition, the issue with the Component Options command no longer occurs. I can now use Component Options to view the IFC attributes I populate while keeping my custom dictionaries intact. This approach has completely resolved the issue.

Thank you again, ene_su, for this great insight!

Regarding the AppliedSchemaTypes dictionary, I verified the following:

  1. When assigning an IFC classification manually through SketchUp’s graphical interface, the AppliedSchemaTypes dictionary is automatically created.

  1. When assigning an IFC classification via the Ruby API, the dictionary is also automatically generated at the time of classification.

Here’s an example of the code I use to classify a component via API:

schema_ifc = "IFC 2x3"
tipo_ifc = "IfcReinforcingBar"
# Add the classification to the definition
@comp_def_parent.add_classification(schema_ifc, tipo_ifc)
# Populate CrossSectionArea
ifc_dict = @comp_def_parent.attribute_dictionary("IFC 2x3", false)
cross_section_dict = ifc_dict.attribute_dictionary("CrossSectionArea", true)
cross_section_dict.set_attribute("IfcAreaMeasure", "value", 201.06) if cross_section_dict

When I use this code to classify the component, the IFC 2x3 dictionary is automatically created, along with the necessary sub-dictionaries (e.g., CrossSectionArea).

This is my currently Sketchup version:

Please notice also that, exporting IFC file from Sketchup (w/o using IFC Manager plugin) my personal dictionaries is exported also!

Thank you again for the support and valuable insights. They have been extremely helpful in improving my plugin’s workflow.

Best regards,

1 Like

When I tried this it did not work at first for me. So I loaded the “IFC 2x3” schema into the Classifications collection.

Then I retried it and got true returned but I could not see the dictionaries created.
I finally tagged the instance manually and the dictionaries were created.

I don’t know much myself about Jan @brewsky 's plugin nor the native IFC exporter (except that there is ongoing work on it to better the exports.)

I’ll need to defer on this.

Whenever things act weird, I close the app and restart the machine to clear memory.
Does this happen repeatedly in every session even after a reboot?

1 Like

Just so you know, this means the data isn’t carried over if you go to the Component panel’s In Model section and place a new instance from there.

1 Like

Hi @DanRathbun @ene_su,

thank you again for the feedback.

I’d like to clarify that the anomalous behavior with the Component Options command occurred even before I started classifying the component using the IFC standard. When I first noticed the issue, my plugin’s code did not yet include lines specific to IFC classification, and the component was not classified.

In general, after restarting the application, if I create a simple cube, turn it into a component, and classify it manually, I can see all the dictionaries related to the loaded IFC schemas. The same happens when using my plugin, provided the user has loaded the correct schema. I’m working on improving the plugin to handle cases where the IFC schema is not loaded, avoiding potential errors.

Thanks again for your observation; it helped me better understand the behavior of the command.

Thank you again for your suggestion to work at the instance level. I’m aware that by using this approach, my dictionaries exist only in specific instances. As a result:

  • If I insert a new identical component, it won’t have my dictionaries.
  • However, if I duplicate an existing instance (e.g., by copying it), the duplicate inherits the dictionary. This behavior allows me to continue modifying the duplicated entities with my plugin.

Thanks again for your support; this solution resolved the issue with Component Options and improved the management of my attributes!

Best regards,

1 Like

It’s true, only “dynamic_attributes” dictionaries and custom dictionaries nested beneath the IFC Schema are exported. (In addition, the name of the dictionary should begin with “Pset_”)

This are the options for Ifc Manager (I don’t know if it helps)

1 Like

SketchUp 2024 had some additions for IFC4 export … See:

Release Notes - SketchUp Desktop 2024.0 | IFC

1 Like

Returning to the initial problem, converting the point or vector objects to arrays appears to solve the problem.

[ORIGIN.to_a]
3 Likes

Thank you, @sWilliams, for your response!

I’ve noticed that the issue seems tied to the structure of the attributes being saved. Specifically, if the attribute is a single Geom::Point3d or Geom::Vector3d object, it works perfectly. However, when I save an array containing multiple points or vectors, the “Component Options” tool generates errors and stops functioning correctly.

This behavior suggests that the issue may arise when handling arrays with multiple complex objects like Geom::Point3d or Geom::Vector3d. It seems that SketchUp’s handling of such data types in attribute dictionaries might not fully align with the expectations of the “Component Options” tool.

One alternative I’m exploring is saving the dictionary at the instance level (as suggested by Ene) instead of at the component definition level, which seems to avoid conflicts. Another approach I’m considering is transforming the arrays of points and vectors into arrays of strings before saving them in the dictionary and then reconverting them into their original format when needed.

If you have any thoughts on these approaches or additional insights on how to resolve the issue, I’d be happy to hear them. Thank you again for your help and expertise!

Best regards,

I’m not seeing any failures when the Point3d objects are stored as arrays of numbers. Let me know if I’m overlooking a test case.

model = Sketchup.active_model
defs = model.definitions
first_def = defs[0]

pt1 = Geom::Point3d.new(0,1,1)
pt2 = Geom::Point3d.new(0,1,2)
pt3 = ORIGIN

# This will fail
# arr1 = [pt1, pt2, pt3]
# first_def.set_attribute "SW", "test", arr1

# This will work
arr2 = [pt1.to_a, pt2.to_a, pt3.to_a]
first_def.set_attribute "SW", "test", arr2

#read them back
pts_array = first_def.get_attribute "SW", "test", []
pts = pts_array.map {|pt| Geom::Point3d.new(pt)} # if you need them as Points3d objects
p pts

nil

Here’s the test model which contains a single Dynamic Component
Untitled.skp (103.8 KB)

Actually, the opposite is the case. The SketchUp API itself has no problem saving these API types and getting them back out of an attribute dictionary. (Do @sWilliams test at the console and it can be verified.)
It is the DC extension code that has the problem and makes incorrect assumptions about the types stored in array attributes.

I would foresee a challenge if the type of objects in the array are mixed.

This may work, as the API overrides the Geom::Point3d#inspect and Geom::Vector3d#inspect methods to output a string like: "Point3d(2.34, 5.67, 1.23)" & "Vector3d(2.34, 5.67, 1.23)"

However the API does not define a reciprocal method to convert back (AFAIK).
The following is what I came up with to convert back.

UPDATE: Switched to using #scan with a new Regexp (matching groups of digit and . characters that are not followed by a “d” character.) It now works for whole integers and decimal numbers.
In addition, it also works for the #to_s and #inspect output of numeric arrays.
But a word of warning, … the String#to_l method may be reading the string values in model units. Trying to get my head around this is difficult as the Ruby Console is displaying stuff in model units and confusing the issue.
ADD: I decided to append a " to the end of each numeric string to force String#to_l to read the values as inches.

Within your extension submodule:

  def str_to_point(str)
    Geom::Point3d.new(
      str.scan(/([0-9.]+)[^d]/).flatten.map {|s| (s<<'"').to_l }
    )
  end

  def str_to_vector(str)
    Geom::Vector3d.new(
      str.scan(/([0-9.]+)[^d]/).flatten.map {|s| (s<<'"').to_l }
    )
  end

Or you might use a class refinement:

module CustomNamespace

  module StringConverter

    refine String do

      # Converts the output of Geom::Point3d#inspect back into a Geom::Point3d
      # object. These strings have the format of "Point3d(1, 2, 3)".
      def to_point
        ::Geom::Point3d.new(
          self.scan(/([0-9.]+)[^d]/).flatten.map {|s| (s<<'"').to_l }
        )
      end

      # Converts the output of Geom::Vector3d#inspect back into a Geom::Vector3d
      # object. These strings have the format of "Vector3d(1, 2, 3)".
      def to_vector
        ::Geom::Vector3d.new(
          self.scan(/([0-9.]+)[^d]/).flatten.map {|s| (s<<'"').to_l }
        )
      end

    end # String class refinement
  
  end # refinement module

  module SomeCustomExtensionSubmodule
  
    using StringConverter
    
    # code that uses String#to_point() and/or String#to_vector()
    
  end # extension submodule

end # top-level namespace
1 Like

Okay, I updated the examples above. (twice)

In IFC manager I indeed only include nested dictionaries inside the IFC dictionary and dynamic_attributes, SU_InstanceSet, SU_DefinitionSet (when enabled in settings)

1 Like

could marshalling or json serialization be an option here?

Marshalling likely not as it needs marshall methods defined for API classes which were not defined. (We had discussed this in the past and rejected it for some quirky reason in the Marshall class.)

JSON would be ideal IF the API would support JSON for some of its’ classes.
Since the API does not, I am wondering if a refinement would work since the deserialization parsing is defined within a (JSON) module. (Ie, refinements can only refine classes.)

I’ll need to do some testing later on. (I have some refinements written up already.)

1 Like