Create groups from entities in nested component

I’m importing blocks from Rhino files and want to convert the geometry inside the nested components to groups. I’ve been looking at various topics in this forum and using the Ruby Console I was able to create a group with the instances of the parent component. My new group is not exactly at the same place like the original geometry and contains three components and I can’t figure out how to explode the components before the group is created. Ideally the imported component is being deleted after the new group is created.

Deleting the newly created group crashes Sketchup.

This file is imported into Sketchup. In Rhino it contains three blocks. When imported in Sketchup, the result is a component with three components.

hex.skp (7.6 KB)

Edit: There are two scenarios to be exact. One would be a file with blocks, the other would be a file with a few surfaces. The file with a number of surfaces is also imported as a component, however not nested. In that case the script would only have to explode the component and create a group for every surface.

Here’s my code so far:

model=Sketchup.active_model
ent=model.active_entities
cdef=model.definitions[0]
inst=cdef.instances
group=model.entities.add_group(inst)

I have been using Python every now and then in Rhino / Grasshopper but I have no experience in Ruby scripting so I’d appreciate someone helping me.

Are these components to be identical instances of the same definition ? … or could they be different ?

This challenge is not much about Python vs Ruby per se, but about the hierarchy of the SKP data model.

We’ve asked for a #to_group method to convert a component instance into a group, but it has not yet been implemented. We wish for a complement to the existing Sketchup::Group#to_component() method.

If you are still using SketchUp 2017 this limits and complicates the solution, but it has been discussed many times and there should already be snippets posted.

Ruby API Category Search “convert to group”

Imports are always inserted as a component instance which needs to be exploded and the definition discarded.

2 Likes

Thanks for the reply, Dan.

I’m using Sketchup 2017 since I only really need to make sure a file is imported correctly into Sketchup.

The instances can be the same but also different.

I’ve looked at a few other threads and could not piece it together yet.

The code below will explode the component instance and group the resulting entities. Is this what you are asking for?

model = Sketchup.active_model
entities = model.active_entities
selection = model.selection
components = selection.grep(Sketchup::ComponentInstance)
new_groups = []

if components.empty?
  msg = 'Please select one or more Component Instance before using this tool.'
  UI.messagebox(msg, MB_OK)
  return
end

msg = 'Are you sure want to convert selected Component Instances to Group?'
result = UI.messagebox(msg, MB_YESNO)
if result == IDYES
  components.each do |instance|
    selection.clear
    result = instance.explode
    result.each do |entity|
      if entity.class.superclass == Sketchup::Drawingelement
        selection.add(entity)
      end
    end
    new_group = entities.add_group(selection)
    selection.add(new_group)
    new_groups << new_group
  end
 
  selection.add(new_groups)
end

I created a topic for an extension I was working for called Regroup where I wanted to explode groups and regroup them to fix the local axis. However, after the feedback, I am now fixing the local axis without the need for exploding groups or components.

Anyways here is the topic URL: SketchUp Extension: Regroup

1 Like

Thanks for the script, but I get an error. I’m using Sketchup 2017, not sure if that is a problem?

1 Like

The image below shows a console error for line 10 where a return is located. Eliminate that return to see if the code runs without errors. Remember to run the code only when a Component Instance SketchUp entity is actively selected.

Here is the code without the return in line 10…

model = Sketchup.active_model
entities = model.active_entities
selection = model.selection
components = selection.grep(Sketchup::ComponentInstance)
new_groups = []

msg = 'Are you sure want to convert selected Component Instances to Group?'
result = UI.messagebox(msg, MB_YESNO)
if result == IDYES
  components.each do |instance|
    selection.clear
    result = instance.explode
    result.each do |entity|
      if entity.class.superclass == Sketchup::Drawingelement
        selection.add(entity)
      end
    end
    new_group = entities.add_group(selection)
    selection.add(new_group)
    new_groups << new_group
  end
 
  selection.add(new_groups)
end
1 Like

There is was a conditional expression to return (if components.empty?) as if from within a method, …
but the example code is not wrapped in a method.

1 Like

Maybe the ruby script was running on my side without error because I was using
the Plugin Ruby Code Editor to run the code.

My guess is that the code that you write in that plugin is passed to a method and that is why the return was being activated without error, but not 100% sure.

Thanks DanRathbun for the info!

1 Like

Strange. I ran the code with the default Ruby Console and it worked even when including the return outside of a method. Not sure why is working for me and he is receiving a console error.

Here is another code example where methods are used to see if it works without console errors.

module MartinSiegrist
  module Compo2Group
    class Main
      # # #
      def initialize
        model = Sketchup.active_model
        @entities = model.active_entities
        @selection = model.selection
        @components = @selection.grep(Sketchup::ComponentInstance)
        @new_groups = []
      end

      def activate
        if @components.empty?
          msg = 'Please select one or more Component Instance before using this tool.'
          UI.messagebox(msg, MB_OK)
          exit_tool
          return
        end

        comp_to_group
        exit_tool
      end

      def comp_to_group
        msg = 'Are you sure want to convert selected Component Instances to Group?'
        result = UI.messagebox(msg, MB_YESNO)
        if result == IDYES
          @components.each do |instance|
            @selection.clear
            result = instance.explode
            result.each do |entity|
              if entity.class.superclass == Sketchup::Drawingelement
                @selection.add(entity)
              end
            end
            new_group = @entities.add_group(@selection)
            @selection.add(new_group)
            @new_groups << new_group
          end
          @selection.add(@new_groups)
        end
      end

      def exit_tool
        Sketchup.active_model.select_tool(nil)
      end
      # # #
    end
  end
end

Sketchup.active_model.select_tool MartinSiegrist::Compo2Group::Main.new

Well if the conditional is false, the return is never evaluated.

1 Like

This is a video response explaining why the previous ruby script was receiving a console error.

1 Like

A few comments:

When I first read the code, I thought it is from the original topic owner. Apparently it is from you.
I would call the outer namespace by the real creator’s name of snippet (You).


I don’t see the need to define a tool class and passing an instance of it to Model#select_tool.
A Tool is usually an interactive “process” in which the user does something and continues until she/he chooses another tool.

Here we are only talking about the processing of pre-selected entities. Then the Tool exits itself, digging out another tool that the user does not necessarily need.
In any case, the correct way to exit the Tool is not to force to select the Select Tool, but to leave it up to the user to choose an another Tool.


I don’t see the need to heavily use Sketchup::Selection in actions.
In order to add a “drawingelement(s)” to the group, it does not have to be selected. I also don’t see any reason to add elements then clear the selection in a next iteration.


I would not use the same variable name result. Especially, not if it is inside a condition defined by a same name. (It maybe okay, but just confusing.)


I would use grep to filter out Sketchup::Drawingelement. (Similar, as you did in your “initialize” method.). Nicer, quicker, more efficient.


I would Respect the Undo Stack.


I read somewhere on a forum here: (Dan will know who wrote :wink: )
“Ruby was designed to be multi-paradigm. So there are always multiple ways.”

This is how I would start on my way … for this task. :innocent:

module Dezmo
module Compo2GroupDez
  
  def self.start
    model = Sketchup.active_model
    @entities = model.active_entities
    @selection = model.selection
    components = @selection.grep( Sketchup::ComponentInstance )
    if components.empty?
      msg = 'Please select one or more Component Instance before using this tool.'
      UI.messagebox(msg, MB_OK)
      return
    end
    comp_to_group( model, components )
  end

  def self.comp_to_group( model, components )
    msg = 'Are you sure want to convert selected Component Instances to Group?'
    result = UI.messagebox(msg, MB_YESNO)
    return unless result == IDYES
    @selection.clear
    model.start_operation( "Compo2Group", true )
      components.each do |instance|
        exploded = instance.explode
        to_group = exploded.grep( Sketchup::Drawingelement )
        new_group = @entities.add_group( to_group )
        @selection.add(new_group)
      end
    model.commit_operation
  end

end
end
Dezmo::Compo2GroupDez.start
3 Likes

@indie3d I like the tutorial video how you are in a sidebox and show various apps in the main view.

I disagree here. I often write examples for the questioner and use their name to encourage the use of a top level namespace. Other times I use the namespace Example but add a comment like …

module Example  # <<<-----< replace with your unique namespace identifier

But many times some newbs don’t understand that they need to edit the example and insert a unique namespace of their own.

This I 100% agree with. This scenario is a command not a tool.

2 Likes