The logic of building "Edit" function

Hello, guys!

Some of you might know that I was trying to create different layers (components) by selecting a face in Sketchup, and I managed to do it with help from very kind and professional people here.

At the moment, I want to go further with this process. If you know Revit, or some plugins in Sketchup that allow users to modify a wall build-up, what I want maybe is clear. For example, an interior wall that has 3 layers, and then the user changes it to 5 or 2, whatever number, layers by simply clicking a “Edit” button, in which the setting is on.

I have an idea for this “Edit” function based on what I have done. 1. Create: select face, and set parameters like the number of layers and its thickness, and finally a wall build-up, that is a nested component, is created (This has been achieved). 2. Edit: The user select the wall build-up and click “Edit” resetting the parameters, and a new wall build-up comes with the same location as the previous one. In Ruby, I was thinking about selecting the wall build-up (a nested component), using ‘sel.grep’ select the original face in the component repeating “Create” phase, and ‘erase!’ the selected wall build-up. Here is the code and there are some errors that I have experienced before but it is just a start:

def bim_wall
  mod = Sketchup.active_model # Open model
  ent = mod.entities # All entities in the model
  sel = mod.selection # Current selection
  
    # Default values
  material_1 = "CLT"
  thickness_1_prompt = 100.mm

  #####################  
   setting parameters in UI input box...
 #####################  

  # Get the currently selected component
  selected_component = sel.grep(Sketchup::ComponentInstance).first

  # Check if a component is selected
  if selected_component
    # Get the faces within the selected component
    component_faces = selected_component.definition.entities.grep(Sketchup::Face)
  
    # Retrieve the first face within the component
    if component_faces.any?
      first_face = component_faces.first
      adjusted_face = first_face.reverse!
      
      group_1 = ent.add_group(adjusted_face)
            

   ########### 
   Set the material for the face
   ##########

      # Extrude the face to create a wall with the specified thickness (thickness_1_prompt)
      adjusted_face.pushpull(thickness_1_prompt)

      # Convert the group to a component and set its name and attributes
      inst = group_1.to_component
      inst.definition.name = "#{material_1}" 
      inst.name = thickness_1_prompt.to_s

      
    end
  end
    
    end
  
    # Delete the selected component
    ent.erase_entities(selected_component)
  end

Anybody has any ideas for creating the logic of “Edit” function in Ruby and Sketchup?
Thank you!

I did not see anything like that in your previous posts. Here you are telling us that you created a different “layers” as components and these are nested in other component.

I do not really understand what you have been selecting then? Are you going into the wrapper component and select all tree “layer” component?

As I told you already, the grep method give you an array with a random order of objects determined parameter ( Sketchup::ComponentInstance …Sketchup::Face …etc.). So the selected_component will not necessarily will be that one what you crated first, neither the component_faces.first will be the desired face.
__

Please check your other tread what I have told you about faces assigned to two different entities collection…
__

In this time there is an extra end in your code. And some lines does commented out that should be.
I would suggest you to try the code before you post here, check the Ruby console for errors, try to interpret or post the error messages too.
__

Suggestion

I would like to suggest you to try to do an exercise in SketchUp. Draw this wall assembly using SketchUp native tools. Take a piece of paper (word doc, excel table) write down step by step, what you are doing in detail, I mean very detailed. Yes, I told detailed. The ruby code needs to be contains these steps or “equivalent”, using the proper syntax.)

__

Usually the “Edit” function are implemented with parameters. These parameters can be “hidden” into for example, the name of the component, but much better if you attach it as attribute of entities created by your code.
See e.g.
Class: Sketchup::AttributeDictionary
Class: Sketchup::AttributeDictionaries

Entity #attribute_dictionary-instance_method
Entity #set_attribute-instance_method
Entity #get_attribute

etc. Search for “attribute” in a method list of Ruby API
image

The attribute parameters (key, value pairs) can be:

So, you can “store” a lot kind of information into the “geometries” you created, then retrieve it to use for creating new geometries.
For example, you can attach dictionary to the “start” face and store its vertices position, you can attach dictionary to layer components storing its material… whatever.

1 Like

Hi, Dezmo,

Sorry about that I did not describe the function clear but thank you for mentioning AttributeDictionaries and your overall suggestion that I should have done before.

This time I tried to include AttributeDictionaries in my code to store the information of the selected face. There are two functions: “bim_wall” for creating the wall and “edit_parameters” for editing the wall. In process, the user selects the “start” face and then use “bim_wall” with its setting for thickness as well as material to create a wall. Then the user can select and edit the wall, created by “bim_wall” related to the “start” face, with “edit_parameters”.

To simply in this case, the wall created has just one layer (one component), but ideally, the wall can have more layers (components) as one component (a nested component) for both “bim_wall” and “edit_parameters”. I have done them separately. Here is the full code (I deleted the material application part), you could try it directly. I promise that I tested it countless times… :wink: and it seems ‘legal’ (but I am not 100% sure :sweat_smile: ):

def bim_wall
  mod = Sketchup.active_model # Open model
  ent = mod.entities # All entities in model
  sel = mod.selection # Current selection

  # Default values
  material_1 = "CLT"
  thickness_1_prompt = 100.mm

  prompt = ["Material_1", "Thickness_1"]
  values = [material_1, thickness_1_prompt]
  list = ["Woodpanel|CLT|Timber frame with mineral wool", ""]
  results = UI.inputbox(prompt, values, list, "Set Wall Parameters")

  unless results == false # Check if user clicked "Cancel" or "Close"
    material_1 = results[0]
    thickness_1_prompt = results[1].to_l # Convert input value to length

    # Create a new group for the face(s) selected by the user
    only_faces = sel.grep(Sketchup::Face)
    only_faces.each do |f|
      group = ent.add_group(f)



      # Extrude the face to create a wall with the specified thickness 
      f.pushpull(thickness_1_prompt)

      # Convert the group to a component and set its name and attributes
      inst = group.to_component
      inst.definition.name = "#{material_1}" 
      inst.name = thickness_1_prompt.to_s
      
      # Create an attribute dictionary for the component
      attribute_dict = inst.attribute_dictionary("MyComponentParams", true)
      attribute_dict["material_1"] = material_1
      attribute_dict["thickness_1_prompt"] = thickness_1_prompt
      attribute_dict["selected_face"] = f.persistent_id.to_s
    end
  end
end

def edit_parameters
  mod = Sketchup.active_model # Open model
  sel = mod.selection # Current selection

  # Retrieve the selected component
  selected_component = sel.grep(Sketchup::ComponentInstance).first

  # Check if a component is selected
  if selected_component
    # Retrieve the attribute dictionary of the component
    attribute_dict = selected_component.attribute_dictionary("MyComponentParams")

    # Check if the attribute dictionary exists
    if attribute_dict
      # Retrieve the existing values for material_1 and thickness_1_prompt
      material_1 = attribute_dict["material_1"]
      thickness_1_prompt = attribute_dict["thickness_1_prompt"]

      # Default values for the input box
      default_values = [material_1, thickness_1_prompt.to_s]

      # List of options for the material selection
      list = ["Woodpanel|CLT|Timber frame with mineral wool", ""]

      # Prompt the user to edit the values
      prompt = ["Material_1", "Thickness_1"]
      results = UI.inputbox(prompt, default_values, list, "Edit Parameters")

      # Update the attribute values and component properties
      unless results == false # Check if user clicked "Cancel" or "Close"
        material_1 = results[0]
        thickness_1_prompt = results[1].to_l # Convert input value to length

        attribute_dict["material_1"] = material_1
        attribute_dict["thickness_1_prompt"] = thickness_1_prompt

        # Set the material for the face
 
        
        # Retrieve the selected face
        selected_face_id = attribute_dict["selected_face"]
        selected_face = selected_component.definition.entities.find { |entity| entity.is_a?(Sketchup::Face) && entity.persistent_id.to_s == selected_face_id }

        # Create a new group for the copied face
        group_selected_face = mod.entities.add_group(selected_face)
        copied_face_group = group_selected_face.copy

        # Select the face inside the copied group
        copied_face = copied_face_group.entities.grep(Sketchup::Face).first

        # Set the material for the face
        

        # Extrude the face to create a wall with the specified thickness (LenY)
        copied_face.reverse!.pushpull(thickness_1_prompt)

        # Convert the group to a component and set its name and attributes
        inst = copied_face_group.to_component
        inst.definition.name = "#{material_1}"
        inst.name = thickness_1_prompt.to_s

        # Create an attribute dictionary for the component
        new_attribute_dict = inst.attribute_dictionary("MyComponentParams", true)
        new_attribute_dict["material_1"] = material_1
        new_attribute_dict["thickness_1_prompt"] = thickness_1_prompt
        new_attribute_dict["selected_face"] = selected_face.persistent_id.to_s

        # Erase the original selected component
        selected_component.erase!
      end
    end
  end
end

# Add a menu item for the plugin
UI.menu("Plugins").add_item("BimWall") do
  bim_wall
end

# Add a menu item for editing parameters
UI.menu("Plugins").add_item("Edit Parameters") do
  edit_parameters
end

The key is to store the information of the selected face ‘f’ in “bim_wall” by ‘f.persistent_id’ and to reuse it when run “edit_parameters”.

When I run the function “bim_wall” to create the wall and, after that, modify the wall with the function “edit_parameters”, no errors showed in Ruby Console, but the wall’s coordination after “edit” is based on origin (0, 0, 0) again rather than the previous wall. (Please see image and yes, you mentioned it for me but here it is little bit more complex to fix).

And when I “edit” the wall after “edit”, Ruby Console shows ‘Error: #<TypeError: wrong argument type (expected Sketchup::Entity)>’. These have exceeded the ceiling of my knowledge in Ruby. Help!!:sweat_smile:

I have a vision that if the wall, created with “bim_wall”, has more layers (components) as one component (a nested component) and the “start face” information is stored in AttributeDictionaries, “edit_parameters” can also retrieve the “start face” in that nested component for further edit (create).

Thank you very much for your time and patience!

Here is one example of a nested component that has two layers (components) with your help before

 mod = Sketchup.active_model # Open model
  ent = mod.entities # All entities in model
  sel = mod.selection # Current selection

  # Default values
  material_1 = "CLT"
  material_2 = "Woodpanel"
  thickness_1_prompt = 100.mm
  thickness_2_prompt = 30.mm

  prompt = ["Material_1", "Thickness_1", "Material_2", "Thickness_2"]
  values = [material_1, thickness_1_prompt, material_2, thickness_2_prompt]
  list = ["Woodpanel|CLT|Timber frame with mineral wool", "", "Woodpanel|CLT|Timber frame with mineral wool", ""]
  results = UI.inputbox(prompt, values, list, "Set Wall Parameters")

  unless results == false # Check if user clicked "Cancel" or "Close"
    material_1 = results[0]
    thickness_1_prompt = results[1].to_l # Convert input value to length
    material_2 = results[2]
    thickness_2_prompt = results[3].to_l

    mod.start_operation('Push_test_a', true)

    sel.grep(Sketchup::Face).each do |f|
      group_1 = ent.add_group(f)

      move_vec = f.normal
      move_vec.length = thickness_1_prompt
      tr_move = Geom::Transformation.translation(move_vec)

      group_2 = group_1.copy
      group_2.transform!(tr_move)

      # Set the material for face 1
      

      # Extrude the face to create a wall with the specified thickness
      f.pushpull(thickness_1_prompt)

      # Convert group_1 to a component and set its name and attributes
      comp_1 = group_1.to_component
      comp_1.definition.name = "#{thickness_1_prompt.to_s} #{material_1}"
      comp_1.name = thickness_1_prompt.to_s  #  Consider later #################

      # Set the material for face 2
     
      # Get the face inside group_2
      face_2 = group_2.entities.grep(Sketchup::Face).first

      
      face_2.pushpull(thickness_2_prompt)

      # Convert group_2 to a component and set its name and attributes
      comp_2 = group_2.to_component
      comp_2.definition.name = "#{thickness_2_prompt.to_s} #{material_2}"
      comp_2.name = thickness_2_prompt.to_s  #  Consider later #################
                                                   
      # Create a new group to hold both components
      parent_group = ent.add_group
      parent_group.entities.add_instance(comp_1.definition, comp_1.transformation)
      parent_group.entities.add_instance(comp_2.definition, comp_2.transformation)
      

      parent_comp = parent_group.to_component
      parent_comp.definition.name = "Build-up"  #
      parent_comp.name = "#{material_1} + #{material_2}"
      
      #Erase the original components
      comp_1.erase!
      comp_2.erase!
      #################
      
    end

    mod.commit_operation
  end

Temporary Out of order...

I don’t think I’ll have time to respond properly these days. I can only look at the forum for ten minutes every now and then. In the meantime, I guess others might help.
I hope that within 1-2 weeks I can return to the “usual” 1-2 hours of daily foruming.

1 Like

You already helped me a lot! I cannot express how much I appreciate your help! Hope you enjoy your time!!

1 Like