SketchUp Trim Extension

The SketchUp API documentation is a DSL Reference (that only extends the core features of the Ruby programming language.)

It is not a learning resource and it’s code examples are notorious for being both worthless and containing errors.

To learn to write good Ruby code that works, you need to read good books on Programming Ruby.
My Ruby Learning Resources lists has a list for books that you can download for free. Then you can read them in your spare time, … in study hall, on the school bus, sitting on the toilet, etc.

As I told you in your last topic, … generating trim profiles with code is the hard way to do this.

Your challenge here (ie, multiple followme-extrusion paths) is a big tipoff that your trim profile face will need to be within component instances that are placed (using a geometric transformation) at the beginning of each trim path.

Except this code only works from within a method definition. The statement with the reserved word return causes the code execution to “return” from this statement, to the point in the code where this method was called.

So your main issue is not organizing your code correctly into modules and methods.

I think it is time you reviewed what I’ve written in your previous topic threads.
(Go through your avatar menu, via your profile and go to your activity and topics.)

I believe I gave you the answer in another topic thread. Your UI command would first check the model selection for a pre-selection of edges. IF the selection was empty, then and only then would you display the messagebox telling the user to pre-pick the trim path(s). When the user dismisses the messagebox (doesn’t matter how, IDOK or IDCANEL… doesn’t matter) your command should exit by simply a return statement from your command method …

    def create_base_trim
      model = Sketchup.active_model
      selection = model.selection
      if selection.empty? || selection.grep(Sketchup::Edge).empty?
        UI.messagebox(
          "Please select edges for your trim path(s) and restart command."
        )
        return false
      end
      # The command continues ...
      edges = selection.grep(Sketchup::Edge)
      # Next determine the number of edge paths in edges.
      # More code as needed ...
    end

You never run UI object creation more than once per session.

This means that …

must be wrapped within a conditional statement that will only run once when your extension loads.
Ie, usually at the very end of the extension submodule definition, or at the end of the last file of an extension in the load order …

    if !@loaded
      plugins_menu = UI.menu("Plugins")
      submenu = plugins_menu.add_submenu("ProTrim")
      submenu.add_item("Create Base Trim") { create_base_trim() }
      # Add other menu items here ...
      @loaded = true
    end

Notice, how I show that you should always only call a command method from within the menu item (or UI::Command) proc. The reason is that Proc objects are a snapshot of the current code environment. During development, you’ll be tweaking your methods and reloading the code to redefine those methods. But you cannot redefine a UI object proc without closing SketchUp and restarting the whole thing which is way too time consuming.

This is why UI object creation is always within a conditional block that ensures menu items, commands, toolbars and toolbar buttons are created once once per session. If you don’t do this, extra menu items with the same name get created in the menus.

And since you should only create them once, and will need to be able to update what menu and toolbar commands do … then you must put the working code inside command method(s) and only have the UI command proc(s) call the appropriate method.

I know I’ve showed you how to correctly set up your code before,
because you almost got it correct here in this post

Topic: End of input error - #22 by caydenwilson017, post 22
… except for the bit about calling the uneeded “init()” method.

I don’t understand how you could go from that very well formed code (in that post) to what you posted above, which is not extension code.

No this will not solve the issues you are having as they are issues with code organization which is not SketchUp API specific. (It’s generic programming practice you are having difficulty with.)

The Sketchup::Model#start_operation() paradigm is something you will need to employ at some point in your coding. (Basically anytime your code modifies the model you’ll need to wrap the code within an undo operation.)

If you’d like to use a block form method within your extension submodule, then something like this …

    def with_undo(model, opname, disable_ui = true)
      return unless block_given?
      model.start_operation(opname, disable_ui)
      yield
      model.commit_operation
    end

… and then whenever your code modifies the model you can use a method like this above …

    def extrude_base_trim(trimdef, transgrp, edges)
      model = Sketchup.active_model
      ents = model.active_entities
      grp = nil
      with_undo(model,"Add Base Trim") do
        grp = ents.add_group # added at ORIGIN
        inst = grp.entities.add_instance(trimdef,IDENTITY)
        grp.transform!(transgrp)
        inst.explode
        face = grp.entities.grep(Sketchup::Face).first
        face.followme(edges)
        # If all went well, the group should contain the extruded trim
      end
      return grp
    end

The transgrp transformation would need to be a combination translation (to the start of the first edge in the trim path array) and a rotation to align the group (and therefore the profile instance within it) to be perpendicular to the trim path. You may also need to reverse the instance’s face so that the back face is headed toward the trim path.
The trimdef is a reference to a preloaded component definition containing a trim profile face.
The edges are an array of one of the trim paths.

Keep in mind it’s an example and might need tweaking for your own use.

2 Likes