How do I create a component from an instance I have drawn?

I am trying to create an extension but cannot seem to find how to turn my draw method into a component.

Post a snippet from your code, otherwise we can only guess what might be wrong.

Please first read the wiki post on how to post code in the forum.
(Convert any TABs to 2 spaces for Ruby.)

As a general pattern, you add a group to the model entities, add your drawing elements to the group’s entities, and then make a component from the group.

1 Like

Did you find a solution to your problem?

1 Like

Here’s what I’m working on

#extension.rb
require 'sketchup.rb'
require 'extenions.rb'

  model=Sketchup.active_model
  entities=Sketchup.active_model.entities
  UI.menu("Extensions").add_item("Extension") {
  UI.messagebox("Welcome to Extension. Select your trim, select the path you wish it to extrude on, and click ok.")
  #Method
  draw_trim_profile
    }
  def draw_trim_profile
  #Create variables
  puts"Trim Height"
  trim_height=gets.chomp
  puts"Trim Width"
  trim_width=gets.chomp
  puts"Quarter Round"
  quarter_rount_width=gets.chomp
  quarter_round_height= quarter_rount_width
  #Create points to build the trim profile (new face)
  points=[
    Geom::Point3d.new(0,0,0)
    Geom::Point3d.new(0,0,height)
    Geom::Point3d.new(0,trim_width,trim_height)
    Geom::Point3d.new(0,trim_width+quarter_rount_width,0)
    Geom::Point3d.new(0,trim_width+quarter_rount_width,quarter_round_height)
    Geom::Point3d.new(0, trim_width,quarter_round_height)
  ]
  face=entities.add_face(points)
  #Modeling the new trim profile (calling methods to entity collection)
  new_face.material=[220,220,220]
  new.face.followme(edge)
  end

@caydenwilson017

  1. Do not double post. It is a violation of the forum guidelines.
    Please fix the first, and delete the second.

  2. As I already said above, please post code properly in these forums.
    Read: [How to] Colourize code on the forum? (wiki)

  3. All of your code needs to be within a toplevel author namespace module.
    Then each one of your extensions should be in it’s own submodule inside your namespace module.

  4. These statements …

    require 'sketchup.rb'
    require 'extenions.rb'
    

    … are not needed as SketchUp loads them automatically before loading extensions.
    (This has been done since SketchUp version 2014 and it’s use of Ruby 2.x.)

  5. Use an extension registrar filename that is unique. The “Plugins” folder file space is a shared space. Your extension registrar script (in the “Plugins” folder,) should be named like:
    "CaydenWilson_TrimProfiler.rb", and the extension subfolder needs to have the same name (without the “.rb” extension,) ie: folder "CaydenWilson_TrimProfiler"

    See: class SketchupExtension

    The filenames within your extension subfolder(s) can be whatever you wish. Ie: "TrimProfiler_main.rb" or "main.rb", etc. One of these files will be the loader file that loads all the rest of the extensions files (if there are any.)

  6. Do not open a model messagebox during SketchUp startup. It will block the startup cycle and the user will have to dismiss the messagebox manually. (A good way to anger users.)
    Instead, use a UI::Notification balloon (that can slide into view down in the lower right corner) if you really must inform the user of an important fact during startup.

  7. You cannot use gets in SketchUp’s embedded Ruby, because standard IO is not like in a normal Ruby shell (IRB.) SketchUp’s Ruby Console has hijacked it.
    Instead, you must use UI.inputbox method to display entry boxes or dropdown select controls for the user.

  8. As you work on an extension, you’ll be reloading it manually as your make changes to methods.
    This means you must protect the UI building (commands, menus, toolbars) so they only get created once when your module loads the first time.
    This will be your pattern …

# encoding: UTF-8

module CaydenWilson
  module ProTrim

    extend self

    # CONSTANTS go here ...

    # Module variables go here ...

    def draw_trim_profile
      # Method code goes here ...
    end

    # More methods go here ...


    if !@loaded

      # Create commands, submenus, menu items
      #   and toolbar objects here:
      UI.menu('Extensions').add_item('CW Designs: ProTrim') {
        draw_trim_profile()
      }

      @loaded = true
    end

  end
end
  1. A few notes on some coding …
points=[
  Geom::Point3d.new(0,0,0)
  Geom::Point3d.new(0,0,height)
  Geom::Point3d.new(0,trim_width,trim_height)
  Geom::Point3d.new(0,trim_width+quarter_rount_width,0)
  Geom::Point3d.new(0,trim_width+quarter_rount_width,quarter_round_height)
  Geom::Point3d.new(0, trim_width,quarter_round_height)
]

… will raise a SyntaxError. The elements of array literals must be separated with a comma.

In SketchUp API many methods that take points will also take 3 element arrays as synonymous with a point (or a vector.) So you do these kind of things …

points=[
  [0,0,0],
  [0,0,height],
  [0,trim_width,trim_height],
  [0,trim_width+quarter_rount_width,0],
  [0,trim_width+quarter_rount_width,quarter_round_height],
  [0, trim_width,quarter_round_height]
]

If you really want to map the arrays to actual Geom::Point3d objects then afterward call …

points.map! {|a| Geom::Point3d.new(a) }

… and now each member is converted to a Geom::Point3d object.


This code …

face=entities.add_face(points)
#Modeling the new trim profile (calling methods to entity collection)
new_face.material=[220,220,220]
new.face.followme(edge)

Has several reference errors. You create face , but then refer to it as new_face and then new.face. (Beware that “new” is the name of a Ruby class’ constructor method and should never used for a variable name.)

:bulb:

Back to the original question …

You do not draw an instance. You add a new ComponentDefinition to the DefinitionList collection.
Then you add entities to the definition’s entities collection. And lastly you add an instance of this new definition to the model’s entities collection.

If you wish to modify a component instance that already exists, then you modify it’s definition’s entities. The result will be that all of that definition’s instances will be modified.

If you only wish to modify the single instance, then you’ll need to make it unique (which clones it’s definition to make a copy.) Then you’ll modify that new definition (which means you must access the definition after the call to #make_unique), ie …

new_cdef = inst.make_unique.definition
new_ents = new_cdef.entities

So, making unique leaves all the instances of the old definition as they were.

Here’s what I did so far…

#protrim.rb
module CaydenWilson
  module ProTrim
    extend self
        #Creating a toolbar icon
        toolbar = UI::Toolbar.new "ProTrim"
        cmd = UI::Command.new("ProTrim") {
        }
        cmd.small_icon = "ToolPencilSmall.png"
        cmd.large_icon = "ToolPencilLarge.png"
        cmd.tooltip = "ProTrim Toolbar"
        cmd.status_bar_text = "Loading ProTrim"
        cmd.menu_text = "ProTrim"
        toolbar = toolbar.add_item cmd
        toolbar.show
        #Creating menu buttons for extenion and its functions
        menu=UI.menu('Extensions')
        menu.add_item("ProTrim") {puts 'Base Trim'}
        menu.add_item("ProTrim") {puts 'Door/Window Trim'}
        menu.add_item("ProTrim") {puts 'Crown Molding'}
        #Creating our extension
        model=Sketchup.active_model
        entities=Sketchup.active_model.entities
        UI.menu("Extensions").add_item("ProTrim") {
        notification=UI.notification.new(sketchup_extension, "Welcome to ProTrim. Select your trim, select the path you wish it to extrude on, and click ok.")
        notification.show
        #Method
        draw_trim_profile
        draw_casing_profile
        draw_molding_profile
          }
      #Creating draw_trim_profile method
      def draw_trim_profile
        puts"Trim Height"
        trim_height=UI.inputbox
        puts"Trim Width"
        trim_width=UI.inputbox
        puts"Quarter Round"
        quarter_rount_width=UI.inputbox
        quarter_round_height= quarter_rount_width
        #Create points to build the trim profile (new face)
        points=[
          [0,0,0],
          [0,0,height],
          [0,trim_width,trim_height],
          [0,trim_width+quarter_rount_width,0],
          [0,trim_width+quarter_rount_width,quarter_round_height],
          [0, trim_width,quarter_round_height]
        ]
        points.map! {|a|Geom::Point3d.new(a)}
        face=entities.add_face(points)
        #Modeling the new trim profile (calling methods to entity collection)
        trim_texture=[220,220,220]
        face.material=[trim_texture]
        Sketchup::Material.back_material=[trim_texture]
        #Creating a path for trim to follow
        selection = model.selection
        face.followme(selection)
      end
      #Creating draw_casing_profile method
      def draw_casing_profile
        puts"Casing Depth"
        casing_depth=UI.inputbox
        puts"Casing Width"
        casing_width=UI.inputbox
        #Building a new casing profile (face) based on the input collected
        points=[
          [0,0,0],
          [0,0,casing_depth],
          [casing_width,0,casing_depth],
          [casing_width,0,0]
        ]
        points.map! {|a|Geom::Point3d.new(a)}
        face=entities.add_face(points)
        casing_texture=[220,220,220]
        face.material=[casing_texture]
        Sketchup::Material.back_material=[casing_texture]
        selection=model.selection
        face.followme(selection)
      end
      #Creating draw_molding_profile method
      def draw_molding_profile
        puts"Molding Drop"
        molding_drop=UI.inputbox
        puts"Molding Projection"
        molding_projection=UI.inputbox
        puts "Cornice Size (Inches)"
        molding_cornice_height=UI.inputbox
        molding_corice_width=molding_cornice_height
        molding_crown
        #Building a new casing profile (face) based on the input collected
        points=[
          [0,0,0],
          [0,0,casing_depth],
          [casing_width,0,casing_depth],
          [casing_width,0,0]
        ]
        points.map! {|a|Geom::Point3d.new(a)}
        face=entities.add_face(points)
        casing_texture=[220,220,220]
        face.material=[casing_texture]
        Sketchup::Material.back_material=[casing_texture]
        selection=model.selection
        face.followme(selection)
      end
      if !@loaded
        UI.menu('Extensions').add_item('ProTrim') {
          draw_trim_profile()
        }
        @loaded=True
    end
  end
end

Thank you for correcting your code post above …

:+1:

2 Likes
  module CustomWindow
    extend self
    #Creating a menu icon
    menu = UI.menu('Extensions')
    menu.add_item("CustomWindow") { puts 'CustomWindow' }
    extensions_menu=UI.menu("Extensions")
    item=extensions_menu.set_validation_proc(item) {
      if Sketchup.is_pro?
        MF_ENABLED
      else
        MF_GRAYED
      end
    }
      #Adding item to Sketchup menu
      tool_menu=UI.menu("Tools")
      tool_menu.add_item("CustomWindow")
      #Creating a notification box with an icon and tooltip
        UI::Notification.new(sketchup_extension,  "/path/to/icon","icon Tooltip")
        puts "CustomWindow: #{notification.tooltip}"
        notification.icon_tooltip="icon Tooltip"
        notification.message="Launched CustomWindow"
        #Closing the notification with a button
        notification.on_dismiss("Close") do |notification, title|
          puts "The user pressed [#{Dismiss}] with message #{notification.on_dismiss_title}"
        end
        notification.show
        #Creating method
        puts"Width (Inches)"
        width=UI.inputbox
        puts"Height (Inches)"
        height=UI.inputbox
        puts"Casing Width"
        casing_width=UI.inputbox
        depth=4
        #Fixed window creation
        puts"Fixed Window"
        fixed=UI.inputbox
        if fixed=True
          puts"Number of Panes"
          pane_number=UI.inputbox
          grill_width=3/4
          grills=pane_number-1
          sash=1.5
          pane_width=(width-casing_width*2+sash*2+grills*grill_width)/pane_number
          pane_height=(height-casing_width*2+sash*2+grills*grill_width)/pane_number
        #Double hung window creation
        puts"Double Hung"
        double=UI.inputbox
        if double=True
          puts"Number of Panes"
          pane_number=UI.inputbox
          grill_width=3/4
          grills=pane_number-1
          sash=1.5
          offset=1
          pane_width=(width-casing_width*2+sash*2+grills*grill_width)/pane_number
          pane_height=(height-casing_width*2+sash*3+3/4+grills*grill_width)/pane_number
        model=Sketchup.active_model
        entities=model.active_entities
      #Creating points to build the model
      pts=[]
      #[x,y,z] x-width, y-depth, z-height
      #front face border points
      pts[1]=[0,0,0],
      pts[2]=[width,0,0],
      pts[3]=[0,0,height],
      pts[4]=[width,0,height],
      #back face border points
      pts[5]=[0,depth,0],
      pts[6]=[width,depth,0],
      pts[7]=[0,depth,height],
      pts[8]=[width,depth,height],
      #Face casing points
      pts[9]=[casing_width,0,casing_width],
      pts[10]=[width-casing_width,0,casing_width],
      pts[11]=[casing_width,0,height-casing_width],
      pts[12]=[width-casing_width,0,height-casing_width],
      #Creating the face
      face=entities.add_face(pts)
      #Creating the materials
      materials=model.materials[0]
      #Glass
      material=materials.add('Window Glass')
      material.alpha=5
      puts material.name
      puts material.display_name
      type=material.solid
      material.save_as(glass.skm)
      #Window Interior
      material=materials.add('Window Interior')
      material.alpha=100
      puts material.name
      puts material.display_name
      type=material.solid
      material.save_as(windowinterior.skm)
      #Window Exterior
      material=materials.add('Window Exterior')
      material.alpha=100
      puts material.name
      puts material.display_name
      type=material.solid
      material.save_as(windowexterior.skm)
      #Coloring Faces
      face.back_material="Window Interior"
      material=face.back_material
      status=face.back_material="Window Interior"```

Your last post declares a module, uses the extend self statement (which extends the module with it’s own methods,) but you’ve not defined any methods.

Also, there is no end at the bottom to close the module block.

The doc clearly says the argument is a Float between 0.0 and 1.0

Again the docs tell you the argument is a text String for the path where the .skm file is to be saved, not just the filename.

Never assume the current working directory is where you want it. Other plugins could have changed it.
It always best to specify full absolute pathnames.

… is incorrect. You’ve referenced the “Extensions” menu twice, and failed to properly get the item reference when you created it.

Get the item reference like so …

    #Creating a menu icon
    menu = UI.menu('Extensions')
    item = menu.add_item("CustomWindow") { puts 'CustomWindow' }
    menu.set_validation_proc(item) {
      if Sketchup.is_pro?
        MF_ENABLED
      else
        MF_GRAYED
      end
    }

… not that this has much effect as there are no more Make editions since 2017, so most everything will be running under a Pro edition.

But your menu command does nothing except send the text “CustomWindow” to STDOUT (which noone will see unless they have the Console window open.

This is not how the UI.inputbox method works. It needs a set of arguments to know what to display for the caption, it’s control labels, and the default values to stuff into the entry boxes. It also returns an array of the edited values (or false if the user cancelled the inputbox.)

    def get_window_size
      caption = "Window Size"
      prompts = [ "Width (Inches)", "Height (Inches)", "Casing Width" ]
      defaults = [ 30.inches, 60.inches, 8.inches ]
      results = UI.inputbox( prompts, defaults, caption )
      return false if !results
      width, height, depth = results
    end

You really need to begin reading the API documentation and studying the examples.

There are full extension examples by the SketchUp Team in the Extension Warehouse.
One of them is called “Window Maker”.

1 Like

First I just want to say thank you for all the help you’ve provided. I’m probably taking off a bit more than I can chew, but I am willing to do my best to make this work. To sum up my big idea, I’m trying to make extensions to automate modeling home parts such as windows, doors, trim, cabinets, and stairs. I’m fifteen and started a business building CAD and rendering those models for custom home builders, and with school it is hard to complete projects, so this new software is kind of my last ditch effort to take on more business. As far as your advice goes…

I’ve been trying to use the API guide, I just don’t know for sure what some of it means and am very new to it. If there’s some way to understand it better or any tips you may have please let me know.

Could you provide me with the SketchUp example extension names? I didn’t know they existed and would love to use them

Thanks again!

Well first of all, the API is an extension to Ruby language. It really helps to learn Ruby first.
There is a pinned topic here in this category I made just for people like yourself …

There are quite a few free downloadable books for you to read in your spare time (on the bus, on the “pot” etc.)


Secondly, the SketchUp API dictionary is a technical reference and not a learning text. It’s code examples are notorious for being frivolous or erroneous.

There is also much left unsaid in the API documentation (ie, tips and tricks and pitfalls, etc.) So when things act weird, come here and enter this category, and then use the :mag: menu (upper right) to search only this category. Chances are someone else has already run into the same “challenge”.

I strongly suggest first reading the primers on Ruby and then starting out with simple snippets in the console (or one of the console editor extensions) to get the feel of coding geometry.

Then move on to tasks a bit more complex, little by little. Otherwise you will become overwhelmed.

When you are finally ready to start a SketchUp extension, make sure you read and understand the organization of an extension’s files, and the SketchupExtension class …

The biggest thing you need to understand about embedded programming for an application is that it is event driven programming.

The code is sitting in memory waiting for the user to cause an event that fires off parts of your code.
It might be a simple thing like a new model was loaded, or the user clicked a toolbar button or made a menu choice.

Your code’s methods react to the events.


There are quite a few others that have already done all of these things in various modeling extensions, … so it is possible.

Well more power to ya’ … being young it’s often easier to learn new things. I encourage you to learn correctly instead of skipping ahead and losing your way. What I mean is build a solid general programming and Ruby syntax foundation instead of hacking away at a complex extension.

One of the best things to learn is coding style. There are some guides out there.
But basically Ruby style is readability. You do not help the interpreter by leaving out spaces.
(Spaces actually help the interpreter divide the code into parsing tokens.)

A blank line takes up only 2 invisible characters.

Likely others at their EW store page.

Also there are example repository on GitHub …

Also I myself have posted quite a few example right here in this category.
Do a search on “[Example]” or “[code]” etc.


In your last two code posts … you defined UI objects outside the if !@loaded block at the end of your module code.

These definitions need to be moved to the end, inside that conditional block so that they only are created once when the module loads the first time.

You will find yourself editing methods and reloading the module as you develop. (Ruby is a dynamic language which means you can modify modules, classes and their methods at any time by reloading the code files. See the global load method.)

Also when the blocks for menu and toolbar commands are defined, you’ll want to call a method within them so that these method(s) can be redefined as you encounter bugs and fix them, reload, text, find bug, fix, reload, … repeat.
If the command block comes before the method definition that it calls, Ruby may raise an exception.

I’d recommend people refer to our GitHub examples over these older examples. They are due for a rewamp and doesn’t always show of best practices.

Well file issues. :wink: