How to create a hole in a surface?
-
How to create a hole in a surface?
- Ruby script: tells user to select a surface area by rectangle (pink)
- Ruby script: then creates a circle at the center of the selected surface
group = entities.add_group entities = group.entities circle = entities.add_circle(@pt15, @vec3, @Ldia, 24) base = entities.add_face(circle)
What code do I need to add to the script, that recognizes the selected surface, so I can have the circle cut a hole out of the selected surface?
-
Entities#add_face()
is bugged with an edge array as argument. That is what is returned from theEntities#add_circle()
method, not the curve object itself.So try:
base = entities.add_face(circle[0].curve)
or:
faces = entities.grep(Sketchup::Face) num_added = circle[0].find_faces() found = entities.grep(Sketchup::Face) - faces base = found.empty? ? nil : found.first
-
is this what you mean?
draw a cube, select any face, run this code...
mod = Sketchup.active_model ents = mod.active_entities sel = mod.selection face = sel.grep(Sketchup;;Face)[0] cent = face.bounds.center norm = face.normal dia = 10 segs = 24 ents.add_circle(cent, norm, dia, segs) sel.clear sel.add(ents.to_a.last) sel[0].erase!
john
-
Thanks! Dan & John for your help, I choose to use Johns code, which works fine. This precipitates a follow up question.
Currently I have replicated this code 4 times in my script, because each Recessed Light occupies a different 3D point location ([highlight=#ffff00:3kwdwn7k]highlighted in yellow[/highlight:3kwdwn7k])
Is there an alternative shortcut in Ruby that would make it unnecessary to replicate 95% of the same code over and over again for just 3 - 3d point location references?cheers!
PS: I will add this useful code snippet to my Ruby Snippet file for future reference
-
Put the repeated code in its own method and pass the variables to it.
-
Do not ever use global variables for plugin variables!
Never mind that examples use them. That doesn't make it okay.Here is some sample code for dealing with options, saving them to objects and across sessions:
# encoding; UTF-8 module Author;;SomePlugin ### AT TOP OF EXTENSION SUB-MODULE; # Extend the sub-module with itself. (This is now preferable # to declaring an anonymous singleton proxy class instance.) # The methods defined will now be available without need to # qualify them with the module as a receiver.) extend self # Define the keyname for saving and reading extension # options to and from Windows registry or Mac plist; @@extkey = Module;;nesting[0].name.gsub(';;','_') # Given that, at the top of your plugin sub-module; # @@prompts is an array of inputbox prompts, and # @@defaults is an array of inputbox default choices ... @@opts_caption = "Recessed Light parameters" @@dict_suffix = "_Parameters" # is appended to @@extkey # Initialize hashes for the plugin options; @@opts ||= {} @@prev ||= {} # Load the options from previously saved choices; # (If not yet saved, use the indexed value from @@defaults) @@prompts.each_with_index {|opt,i| @@prev[opt]=( @@opts[opt]= Sketchup;;read_default( @@extkey, opt, @@defaults[i] ) ) } ### SOME HELPFUL METHODS; # save_options_to_defaults() # A method to save the options hash to the # Windows registry or plist file on the Mac. def save_options_to_defaults() @@opts.each_pair {|optname,optval| Sketchup;;write_default( @@extkey, optname, optval } } end # save_options_to_dictionary(obj) # A method to save the options hash to an # attribute dictionary attached to an object. def save_options_to_dictionary(obj) dict_name = @@extkey + @@dict_suffix @@opts.each_pair {|optname,optval| obj.set_attribute( dict_name, optname, optval ) } rescue return false else return true end # get_options_from_dictionary(obj) # A method to return options from an object's # attribute dictionary, as a Ruby hash. # Does not create dictionary nor attributes. # Default option values are used for any missing attributes. def get_options_from_dictionary(obj) dict_name = @@extkey + @@dict_suffix hash = {} @@opts.each_pair {|optname,optval| hash[optname]= obj.get_attribute( dict_name, optname, optval ) } return hash end # has_options_dictionary?(obj) def has_options_dictionary?(obj) return false if !obj.respond_to?(;attribute_dictionaries) || obj.attribute_dictionaries.nil? dict_name = @@extkey + @@dict_suffix obj.attribute_dictionaries[dict_name] ? true ; false end # get_options_from_user() # Displays the parameters inputbox, and processes results. # Sets previous options to @@prev hash, and new choices to # @@opts hash. # Returns nil if user cancels the input box, or # a hash of boolean change flags with same keys as @@opts. def get_options_from_user() # Display the inputbox; results = UI;;inputbox( @@prompts, @@defaults, @@opts_caption ) return nil unless results # Check for any changes; changed = {} results.each_with_index {|val,i| @@prev[@@prompts[i]]= @@opts[@@prompts[i]] if @@opts[@@prompts[i]] != val changed[@@prompts[i]]= true @@opts[@@prompts[i]]= val # set new value else changed[@@prompts[i]]= false end } # Now the hash @@opts is loaded with the results, # and @@prev hash holds all the previous choices. changed # Return a hash of boolean change flags. end ### EXAMPLE - DOWN IN OTHER CODE; # Checking results from parameters inputbox; changes = get_options_from_user() if changes.nil? # user cancelled the inputbox elsif changes.any? # iterate the changes hash to find those members # set true, which are those that were changed. # Or just test certain options if changed; if changed['Light Array Type'] if @@prev['Light Array Type'].to_i < 3 && @@opts['Light Array Type'] == '4 Lights' # do some task ... else # another test ? end else # skip this or do something else end else # The user just accepted the default choices end end # plugin sub-module
-
@tomato said:
... each Recessed Light..
whenever you have more than one of anything, you should just make a component and place the copies...
they can even have 'cuts opening' set on them to save your that issue...
john
-
@dan rathbun said:
Do not ever use global variables for plugin variables!
Never mind that examples use them. That doesn't make it okay.I use global variables only during script development.When I do the math that locates points, I use the Ruby Console to varify that I have made no mistake in point location, this comes in very handy when one has been coding for 8 hours, lots of typing mistakes can creep in when you one gets older
Thanks!!
-
@sdmitch said:
Put the repeated code in its own method and pass the variables to it.
Thanks! I'm aware of "making my own method" and it has not escaped me from looking at your scripts, regardless I have never been able to get any of my attempts to work ... hence I gave up! What would be really great if some You tube videos existed that use the Ruby Sketchup API, demonstrating the process and pitfalls .....just thinking out loud!
-
Agian, there is no good reason to ever be using globals.
Not ever. They are not needed, ever! Zilch! Nada!@tomot said:
@sdmitch said:
Put the repeated code in its own method and pass the variables to it.
I'm aware of "making my own method" ..., regardless I have never been able to get any of my attempts to work... hence I gave up!
I just gave you a proper example of how to use methods, and how to use module variables instead of globals.
Don't just "give up", ask specific questions about specific "attempts".
(Ie, your statement is a bit vague.)What is it that you are having trouble understanding about using methods ?
-
@unknownuser said:
The last pic I posted above shows the code snippet
- Cuts a single hole for a recessed light
- Draws the recessed light
- At 3d point location @ptw1616
The other 3 recessed lights require either the same repeating code with a different 3d point location, Or as sdmitch suggested a method that passes the variables to it. It appears to me that there is only 1-3d point variable that needs to be passed on to this new method? I don't know now what if any of the code I currently have, will still be relevant, or how and where this new method will fit into the remainder to my script.
I hope this is somewhat less vague!
# Draw Circle on selected ceiling geometry - Cut Hole > mod = Sketchup.active_model > ents = mod.active_entities > sel = mod.selection > face = sel.grep(Sketchup;;Face)[0] > ents.add_circle(@ptw1616, @vec3, @Ldia, 24) > sel.clear > sel.add(ents.to_a.last) > sel[0].erase! > > # Draw Recessed Light > tube = entities.add_group > tube_inner = tube.entities.add_circle(@ptw1616, @vec3, @Ldia-@Lthick , 24) > tube_outer = tube.entities.add_circle(@ptw1616, @vec3, @Ldia , 24) > cross_section_face = tube.entities.add_face tube_outer > inner_face = tube.entities.add_face tube_inner > tube.entities.erase_entities inner_face > cross_section_face.pushpull -@Ldepth, false
Draw Circle on selected ceiling geometry - Cut Hole
mod = Sketchup.active_model ents = mod.active_entities sel = mod.selection face = sel.grep(Sketchup;;Face)[0] ents.add_circle(@ptw1616, @vec3, @Ldia, 24) sel.clear sel.add(ents.to_a.last) sel[0].erase! # Draw Recessed Light tube = entities.add_group tube_inner = tube.entities.add_circle(@ptw1616, @vec3, @Ldia-@Lthick , 24) tube_outer = tube.entities.add_circle(@ptw1616, @vec3, @Ldia , 24) cross_section_face = tube.entities.add_face tube_outer inner_face = tube.entities.add_face tube_inner tube.entities.erase_entities inner_face cross_section_face.pushpull -@Ldepth, false [/code:35jvptxs]
-
I have no idea how you determine what @ptw1616 is but another option might be creating an array of the locations like this
# Draw Circle on selected ceiling geometry - Cut Hole mod = Sketchup.active_model ents = mod.active_entities sel = mod.selection face = sel.grep(Sketchup;;Face)[0] @vec3 = face.normal; @Ldia=1.5; @Lthick=0.25; @Ldepth=0.25 locations=[[1,2,0],[3,2,0],[1,4,0],[3,4,0]] locations.each{|loc| @ptw1616=loc ents.add_circle(@ptw1616, @vec3, @Ldia/2, 24) ents.to_a.last.erase! # Draw Recessed Light tube = ents.add_group tube_inner = tube.entities.add_circle(@ptw1616, @vec3, @Ldia/2-@Lthick , 24) tube_outer = tube.entities.add_circle(@ptw1616, @vec3, @Ldia/2 , 24) cross_section_face = tube.entities.add_face tube_outer inner_face = tube.entities.add_face tube_inner tube.entities.erase_entities inner_face cross_section_face.pushpull -@Ldepth, false }
-
@unknownuser said:
I have no idea how you determine what @ptw1616 is but another option might be creating an array of the locations like this
All the points are per-determined in my script. They relate to the user selected area.
Various combination of points make a pattern of lights the user can choose from in the Light Array Type Dialog Box.
Your understanding of Ruby has helped me tremendously. Furthermore I did not realize the importance of the last "}" .....which I originally thought might have been a typo........ Yikes!!!
Advertisement