My First Extension

Hey all,

I’m new to these forums so if this post is in the wrong place or unrelated, let me know.

Anyway, I’ve been using SketchUp for a couple months now and I wanted to try my hand at writing an extension so I wrote an extension that provides “X-ray” copies of selected groups and adds them to a new layer called “Individual_Xray.”

If you want to check it out, here’s the link: GitHub - rkildare/Indiv_Xray: Sketchup addon for creating "Xray" copies of individual groups.

Thanks.

1 Like

Unfortunately this is not an extension rather Snippets.
It is violating one of the main rules (beside many others…):
“Safely namespace your extension to insulate it from other extensions.”

I would recommend to read e.g. this:
https://developer.sketchup.com/hello-cube

You can also use for example the template for extension from Dan:

1 Like

Ruby uses 2 space indentation. (Please adjust your editor to replace tabs with 2 spaces.)


Dezmo is correct. Only Ruby core (and very limited API) global methods should be defined in the toplevel ObjectSpace. Since everything in Ruby is an object and therefore a descendant of class Object, your toplevel methods will get inherited by EVERY other class and module.


String comparison in Ruby is relatively slow. This means you should avoid the API’s #typename method for comparions and instead use class comparisons …

if obj.is_a?(SketchUp::Face)
  # do something
else
  # do some other thing
end

… OR …

case obj
when Sketchup::Face
  # do something
when Sketchup::Group
  # do something unique
else
  # do some other thing
end

See official examples …

Also, everything should be fully reversable reversible with undo.

Enclose model change code within an undo operation. See …

Components are a little weird in how they should be properly handled. Using this current method for groups on components will not work.

FYI. Groups are just special component instances that are handled differently in the UI. They are not listed in the “In Model” collection of the Component Inspector. And manually editing a group that has multiple instances will automatically make it unique (which clones it’s definition.)

So both kinds of instances (groups or components) can be copied using the Sketchup::Entities#add_instance method.

group_copy = some_entities_collection.add_instance(
  group_instance.definition, group_instance.transformation
)

component_copy = some_entities_collection.add_instance(
  component_instance.definition, component_instance.transformation
)

Of course if you want to move them elsewhere them modify the transformation or supply a new transformation.

Thanks for the awesome feedback!

I had no idea that extensions could inherit methods from each other. I have implimented the proposed namespace changes following the “hello-cube” guide that Dezmo has posted.- Thanks Dezmo.

Tabs have been changed to two spaces and I have now enclosed model changes within “start_operation” and “commit_operation”. -Thanks Dan.

All changes above have been pushed to GitHub.

I’ll have a look at type checking and component handling this weekend. Thanks again for pointing me in the right direction.

“Inherit” isn’t the right word, since Ruby does support single inheritance of classes like other OOP languages and derived classes inherit names from their superclasses. “Share” would be better. There is only one instance of the Ruby interpreter running within SketchUp. All extensions load their code into this single interpreter together. So, at the root global level, they all share the same namespace. Anything that isn’t wrapped in a module or class goes into that shared global namespace where it can collide with anything given the same name by another ill-structured extension!

For the same reason explicitly global variables (names starting with $) should not be used. There are some defined by Ruby itself due to ancient conventions, but even they should be used with caution because they can’t be controlled using module or class namespaces.

1 Like

Generally you are using parenthesis when you shouldn’t (around single conditional expressions following if or unless,) and omitting them when you should always use them (around method parameters for object instance method calls. [Ruby is not one of the languages that forces parenthesized conditional expressions.]
This means most all of the SketchUp API instance methods. Ruby used to spit out warnings to STDOUT when parenthesis were not used for method calls like this. But method calls may omit parenthesis if the method is global in class or module definition scope. Ie, the method is defined by [or mixed into] BasicObject, Kernel, Object, Module or Class. These methods have what is called “reserved word status”. The decision code for the warning slowed things down too much so it was removed.
So, generally when the receiver object is not the implied self and must be stated before the method name using dot notation, then we should always use parenthesis around the parameters.)
Also, never put space(s) between the method name and the opening paren for the parameter list.

This is not yet reflected in the files pushed to GitHub.

Except that your bailout statement needs to be moved before the undo operation so that an operation is not opened in event of an empty selection.

Another thing you can do make your command context sensitive, so it can only be used when the selection is not empty. To do this add a validation proc to your UI::Command object that checks for empty selection and returns MF_GRAYED | MF_DISABLED when it is.

cmd.set_validation_proc {
  Sketchup.active_model.selection.empty? ? MF_GRAYED | MF_DISABLED : MF_ENABLED
}

The reason for the or’ed expression is the slightly different way menu items and buttons are handled on MacOS vs Windows.

You can get a bit fancier and actually test if the selection has groups, etc. …

cmd.set_validation_proc {
  ss = Sketchup.active_model.selection
  if !ss.empty? && !ss.grep(Sketchup::Group).empty?
    MF_ENABLED
  else
    MF_GRAYED | MF_DISABLED
  end
}

… etc.

But try to keep proc evaluation to the minimum if used for toolbar button commands as they get called a lot.

1 Like

Thanks for the clarification Slbaumgartner.

Whoops, I’ll have to keep this all in mind; Ruby is still a little foreign to me so I’m glad to get as much advice as I can.

Haha I changed my tab spacing in VS Code but didn’t actually convert my tabs to spaces! Nice catch.

WHOA! That’s awesome! Thanks for the suggestion.

If anyone is actually following this; these changes are up on GitHub.

1 Like