From a6b2083ce2dc56ac7cd5d4fdb775760289435880 Mon Sep 17 00:00:00 2001 From: Anton Chalakov Date: Mon, 6 Oct 2025 10:07:14 +0300 Subject: [PATCH 1/3] add new test and functionality to decide between entity and model for attribute dictionaries --- ext/attribute_dictionaries.c | 81 +++++++++++++++++++++++++++++ ext/model.c | 4 +- test/test_attribute_dictionaries.rb | 18 +++++-- 3 files changed, 98 insertions(+), 5 deletions(-) diff --git a/ext/attribute_dictionaries.c b/ext/attribute_dictionaries.c index c1b14b4..a5cf12b 100644 --- a/ext/attribute_dictionaries.c +++ b/ext/attribute_dictionaries.c @@ -3,6 +3,20 @@ #include #include +struct AttributeDictionaryStringIterationContext +{ + VALUE key; + SUAttributeDictionaryRef dictionary; +}; + +void Sketchup_AttributeDictionaries_Get_By_String_Iterator(SUAttributeDictionaryRef dictionary, struct AttributeDictionaryStringIterationContext* dictionary_struct) +{ + VALUE output; + GETSTRING(SUAttributeDictionaryGetName, dictionary, output); + if (strcmp(StringValuePtr(output), StringValuePtr(dictionary_struct->key)) == 0) + dictionary_struct->dictionary = dictionary; +} + static VALUE Sketchup_AttributeDictionaries_get(VALUE self, VALUE key) { SUModelRef model = {DATA_PTR(self)}; @@ -33,6 +47,72 @@ static VALUE Sketchup_AttributeDictionaries_length(VALUE self) return ULL2NUM(count); } +static VALUE Sketchup_AttributeDictionaries_delete(VALUE self, VALUE dictionary) +{ + if (rb_ivar_defined(self, rb_intern("@is_model")) == Qtrue && RTEST(rb_iv_get(self, "@is_model"))) + { + SUModelRef model = {DATA_PTR(self)}; + if (rb_type(dictionary) == T_STRING) + { + struct AttributeDictionaryStringIterationContext dictionary_struct = {dictionary, SU_INVALID}; + FOREACH(SUModelGetNumAttributeDictionaries, SUModelGetAttributeDictionaries, SUAttributeDictionaryRef, model, Sketchup_AttributeDictionaries_Get_By_String_Iterator, &dictionary_struct); + if (SUIsInvalid(dictionary_struct.dictionary)) + return Qnil; + SUAttributeDictionaryRelease(dictionary_struct.dictionary.ptr); + size_t ad_count = 0; + size_t ad_len = 0; + SUModelGetNumAttributeDictionaries(model, &ad_len); + SUAttributeDictionaryRef* dictionaries; + SUModelGetAttributeDictionaries(model, ad_len, dictionaries, &ad_count); + return Data_Wrap_Struct(rb_path2class(SKETCHUP_ATTRIBUTEDICTIONARIES), 0, 0, dictionaries); + } + else if (rb_obj_is_kind_of(dictionary, rb_path2class(SKETCHUP_ATTRIBUTEDICTIONARY))) + { + SUAttributeDictionaryRef dictionary_ref = {DATA_PTR(dictionary)}; + SUAttributeDictionaryRelease(dictionary_ref.ptr); + size_t ad_count = 0; + size_t ad_len = 0; + SUModelGetNumAttributeDictionaries(model, &ad_len); + SUAttributeDictionaryRef* dictionaries; + SUModelGetAttributeDictionaries(model, ad_len, dictionaries, &ad_count); + return Data_Wrap_Struct(rb_path2class(SKETCHUP_ATTRIBUTEDICTIONARIES), 0, 0, dictionaries); + } + else + rb_raise(rb_eArgError, "Wrong argument type. Expected String or AttributeDictionary"); + } + else + { + SUEntityRef entity = {DATA_PTR(self)}; + if (rb_type(dictionary) == T_STRING) + { + struct AttributeDictionaryStringIterationContext dictionary_struct = {dictionary, SU_INVALID}; + FOREACH(SUEntityGetNumAttributeDictionaries, SUEntityGetAttributeDictionaries, SUAttributeDictionaryRef, entity, Sketchup_AttributeDictionaries_Get_By_String_Iterator, &dictionary_struct); + if (SUIsInvalid(dictionary_struct.dictionary)) + return Qnil; + SUAttributeDictionaryRelease(dictionary_struct.dictionary.ptr); + size_t ad_count = 0; + size_t ad_len = 0; + SUEntityGetNumAttributeDictionaries(entity, &ad_len); + SUAttributeDictionaryRef* dictionaries; + SUEntityGetAttributeDictionaries(entity, ad_len, dictionaries, &ad_count); + return Data_Wrap_Struct(rb_path2class(SKETCHUP_ATTRIBUTEDICTIONARIES), 0, 0, dictionaries); + } + else if (rb_obj_is_kind_of(dictionary, rb_path2class(SKETCHUP_ATTRIBUTEDICTIONARY))) + { + SUAttributeDictionaryRef dictionary_ref = {DATA_PTR(self)}; + SUAttributeDictionaryRelease(dictionary_ref.ptr); + size_t ad_count = 0; + size_t ad_len = 0; + SUEntityGetNumAttributeDictionaries(entity, &ad_len); + SUAttributeDictionaryRef* dictionaries; + SUEntityGetAttributeDictionaries(entity, ad_len, dictionaries, &ad_count); + return Data_Wrap_Struct(rb_path2class(SKETCHUP_ATTRIBUTEDICTIONARIES), 0, 0, dictionaries); + } + else + rb_raise(rb_eArgError, "Wrong argument type. Expected String or AttributeDictionary"); + } +} + VALUE AttributeDictionaries_Init(VALUE Sketchup, VALUE Sketchup_Entity) { VALUE Sketchup_AttributeDictionaries = rb_define_class_under(Sketchup, ATTRIBUTEDICTIONARIES, Sketchup_Entity); @@ -42,5 +122,6 @@ VALUE AttributeDictionaries_Init(VALUE Sketchup, VALUE Sketchup_Entity) rb_define_method(Sketchup_AttributeDictionaries, "each", Sketchup_AttributeDictionaries_each, 0); rb_define_method(Sketchup_AttributeDictionaries, "length", Sketchup_AttributeDictionaries_length, 0); rb_define_method(Sketchup_AttributeDictionaries, "size", Sketchup_AttributeDictionaries_length, 0); + rb_define_method(Sketchup_AttributeDictionaries, "delete", Sketchup_AttributeDictionaries_delete, 1); return Sketchup_AttributeDictionaries; } \ No newline at end of file diff --git a/ext/model.c b/ext/model.c index 21c5010..164ff06 100644 --- a/ext/model.c +++ b/ext/model.c @@ -26,7 +26,9 @@ static VALUE Sketchup_Model_definitions(VALUE self) static VALUE Sketchup_Model_attribute_dictionaries(VALUE self) { - return Data_Wrap_Struct(rb_path2class(SKETCHUP_ATTRIBUTEDICTIONARIES), 0, 0, DATA_PTR(self)); + VALUE obj = Data_Wrap_Struct(rb_path2class(SKETCHUP_ATTRIBUTEDICTIONARIES), 0, 0, DATA_PTR(self)); + rb_iv_set(obj, "@is_model", Qtrue); + return obj; } VALUE Model_Init(VALUE Sketchup, VALUE rb_cObject) diff --git a/test/test_attribute_dictionaries.rb b/test/test_attribute_dictionaries.rb index ff143cb..d0c186c 100644 --- a/test/test_attribute_dictionaries.rb +++ b/test/test_attribute_dictionaries.rb @@ -14,17 +14,23 @@ def test_object end def test_accessor - skip assert_instance_of(Sketchup::AttributeDictionary, @attribute_dictionaries['GeoReference']) - assert_instance_of(Sketchup::AttributeDictionary, @attribute_dictionaries[@attribute_dictionaries['GeoReference']]) end def test_count assert_equal(3, @attribute_dictionaries.count) end - def test_delete - skip + def test_delete_by_dictionary + assert_equal(3, @attribute_dictionaries.length) + @attribute_dictionaries.delete(@attribute_dictionaries['GeoReference']) + assert_equal(2, @attribute_dictionaries.length) + end + + def test_delete_by_name + assert_equal(3, @attribute_dictionaries.length) + @attribute_dictionaries.delete('GeoReference') + assert_equal(2, @attribute_dictionaries.length) end def test_each @@ -40,6 +46,10 @@ def test_length assert_equal(3, @attribute_dictionaries.length) end + def test_count + assert_equal(3, @attribute_dictionaries.count) + end + def test_size assert_equal(3, @attribute_dictionaries.size) end From 90a2ef60a0c71b2ed65c33b882741781d696bffa Mon Sep 17 00:00:00 2001 From: Anton Chalakov Date: Fri, 10 Oct 2025 08:58:33 +0100 Subject: [PATCH 2/3] add delete for attribute_dictionaries --- README.md | 2 +- ext/attribute_dictionaries.c | 18 ++++++++++++++---- test/test_attribute_dictionaries.rb | 3 +++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0df90c5..7800b9b 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ The SketchUp C API is much more limmited than the ruby api. There is no gui eve # Fully implemented classes * Attribute Dictionary +* Attribute Dictionaries * Color * Component Definition * Definition List @@ -14,7 +15,6 @@ The SketchUp C API is much more limmited than the ruby api. There is no gui eve * Material # Partially implemented classes -* Attribute Dictionaries * Behavior * Component Instance * Drawing Element diff --git a/ext/attribute_dictionaries.c b/ext/attribute_dictionaries.c index a5cf12b..795cd81 100644 --- a/ext/attribute_dictionaries.c +++ b/ext/attribute_dictionaries.c @@ -19,11 +19,21 @@ void Sketchup_AttributeDictionaries_Get_By_String_Iterator(SUAttributeDictionary static VALUE Sketchup_AttributeDictionaries_get(VALUE self, VALUE key) { - SUModelRef model = {DATA_PTR(self)}; SUAttributeDictionaryRef dictionary = SU_INVALID; - enum SUResult result = SUModelGetAttributeDictionary(model, StringValuePtr(key), &dictionary); - if (result != SU_ERROR_NONE) - return Qnil; + if (rb_ivar_defined(self, rb_intern("@is_model")) == Qtrue && RTEST(rb_iv_get(self, "@is_model"))) + { + SUModelRef model = {DATA_PTR(self)}; + enum SUResult result = SUModelGetAttributeDictionary(model, StringValuePtr(key), &dictionary); + if (result != SU_ERROR_NONE) + return Qnil; + } + else + { + SUEntityRef entity = {DATA_PTR(self)}; + enum SUResult result = SUEntityGetAttributeDictionary(entity, StringValuePtr(key), &dictionary); + if (result != SU_ERROR_NONE) + return Qnil; + } return Data_Wrap_Struct(rb_path2class(SKETCHUP_ATTRIBUTEDICTIONARY), 0, 0, dictionary.ptr); } diff --git a/test/test_attribute_dictionaries.rb b/test/test_attribute_dictionaries.rb index d0c186c..0d2522e 100644 --- a/test/test_attribute_dictionaries.rb +++ b/test/test_attribute_dictionaries.rb @@ -15,6 +15,9 @@ def test_object def test_accessor assert_instance_of(Sketchup::AttributeDictionary, @attribute_dictionaries['GeoReference']) + entity = Sketchup.active_model.definitions.find { |d| d.name == 'Heather' } + entity.attribute_dictionary('Age', 42) + assert_instance_of(Sketchup::AttributeDictionary, entity.attribute_dictionaries['Age']) end def test_count From 8e19ea800d5ffede6ab7391ecd732f74726e5069 Mon Sep 17 00:00:00 2001 From: Anton Chalakov Date: Mon, 13 Oct 2025 13:51:35 +0300 Subject: [PATCH 3/3] fix wrong arguments for SUAttributeDictionaryRelease --- ext/attribute_dictionaries.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/attribute_dictionaries.c b/ext/attribute_dictionaries.c index a5cf12b..46627b4 100644 --- a/ext/attribute_dictionaries.c +++ b/ext/attribute_dictionaries.c @@ -58,7 +58,7 @@ static VALUE Sketchup_AttributeDictionaries_delete(VALUE self, VALUE dictionary) FOREACH(SUModelGetNumAttributeDictionaries, SUModelGetAttributeDictionaries, SUAttributeDictionaryRef, model, Sketchup_AttributeDictionaries_Get_By_String_Iterator, &dictionary_struct); if (SUIsInvalid(dictionary_struct.dictionary)) return Qnil; - SUAttributeDictionaryRelease(dictionary_struct.dictionary.ptr); + SUAttributeDictionaryRelease(&dictionary_struct.dictionary); size_t ad_count = 0; size_t ad_len = 0; SUModelGetNumAttributeDictionaries(model, &ad_len); @@ -69,7 +69,7 @@ static VALUE Sketchup_AttributeDictionaries_delete(VALUE self, VALUE dictionary) else if (rb_obj_is_kind_of(dictionary, rb_path2class(SKETCHUP_ATTRIBUTEDICTIONARY))) { SUAttributeDictionaryRef dictionary_ref = {DATA_PTR(dictionary)}; - SUAttributeDictionaryRelease(dictionary_ref.ptr); + SUAttributeDictionaryRelease(&dictionary_ref); size_t ad_count = 0; size_t ad_len = 0; SUModelGetNumAttributeDictionaries(model, &ad_len); @@ -89,7 +89,7 @@ static VALUE Sketchup_AttributeDictionaries_delete(VALUE self, VALUE dictionary) FOREACH(SUEntityGetNumAttributeDictionaries, SUEntityGetAttributeDictionaries, SUAttributeDictionaryRef, entity, Sketchup_AttributeDictionaries_Get_By_String_Iterator, &dictionary_struct); if (SUIsInvalid(dictionary_struct.dictionary)) return Qnil; - SUAttributeDictionaryRelease(dictionary_struct.dictionary.ptr); + SUAttributeDictionaryRelease(&dictionary_struct.dictionary); size_t ad_count = 0; size_t ad_len = 0; SUEntityGetNumAttributeDictionaries(entity, &ad_len); @@ -100,7 +100,7 @@ static VALUE Sketchup_AttributeDictionaries_delete(VALUE self, VALUE dictionary) else if (rb_obj_is_kind_of(dictionary, rb_path2class(SKETCHUP_ATTRIBUTEDICTIONARY))) { SUAttributeDictionaryRef dictionary_ref = {DATA_PTR(self)}; - SUAttributeDictionaryRelease(dictionary_ref.ptr); + SUAttributeDictionaryRelease(&dictionary_ref); size_t ad_count = 0; size_t ad_len = 0; SUEntityGetNumAttributeDictionaries(entity, &ad_len);