WHoops! I made an error and left out the word no from the previous post. (Now fixed.)
I’m still working on the group for the trim, but does this look better? I also have fixed the folder structure and naming, created the register file as shown, and placed icons in a “Icons” folder. Thanks!
module CaydenWilson
module ProTrim
extend self
#Loading in handles
model = Sketchup.active_model
selection = Sketchup.active_model.selection
entities = Sketchup.active_model.entities
materials = Sketchup.active_model.materials
layer_array = Sketchup.active_model.layers
require 'sketchup.rb'
require 'extensions.rb'
def get_points(axis, height, thickness)
case axis
when X_AXIS
[ [0,0,0], [0,0,height], [thickness,0,0], [thickness,0,height] ]
when Y_AXIS
[ [0,0,0], [0,0,height], [0,thickness,0], [0,thickness,height] ]
end
end
def create_trim_material(materials)
trim_material = Sketchup.active_model.materials.add "Trim"
trim_material.color = [red, green, blue]
trim_group.material = trim_material
end
def build_trim(axis, selection, height, thickness)
#Input to gather data for trim
prompts = ["Trim Height: ", "Trim Thickness:", "Quarter Round:", "R:", "G:", "B:"]
defaults = [5.0,0.5,"No",255.0,255.0,255.0]
list = ["","","Yes|No","","",""]
input = UI.inputbox prompts, defaults, list, "ProTrim"
return unless input
height,thickness,quarter_round,red,green,blue=input
if quarter_round == 'Yes'
quarter_round_width = 0.75
quarter_round_height = 0.75
end
message = "Select edges for your trim path."
result = UI.messagebox(message, MB_OKCANCEL)
if result == IDCANCEL
return
end
pts=get_points(axis, height, thickness)
#Creating a face using array
face = entities.add_face(pts)
edges = face.edges
connected = face.all_connected
face.back_material = "Trim"
material = trim.back_material
face.material = trim
#Selecting edges (path) to extrude on
face.followme( selection.grep(Sketchup::Edge) )
end
def create_trim_layer(layer_array, entities)
new_layer = model.layers.add("Trim")
model.active_layer = new_layer
name = trim
visable = True
end
if !@loaded
submenu = UI.menu("Extensions").add_submenu("ProTrim")
toolbar=UI::Toolbar.new "ProTrim"
cmd = UI::Command.new("Build Trim") { init() }
cmd.small_icon = File.join(__dir__, "Icons", "IconSmall.png")
cmd.large_icon = File.join(__dir__, "Icons", "IconLarge.png")
cmd.menu_text = "ProTrim"
toolbar.add_item cmd
toolbar.show
submenu.add_item(cmd)
# Set the @loaded var to true so this block is only evaluated ONCE:
@loaded = true
end
end
end
Firstly, many of the examples you might see, are very old and violate good programming style rules that have been accepted since.)
It looks a little better, but you’ve still missed a few places without spaces around =.
(This line [above] is also indented, but should not be.)
Secondly, whenever you call a method upon an object with dot notation, you should use parenthesis around the argument list. (The call to the UI::Toolbar.new
constructor [above] is a prime example where parenthesis should always be used.)
Only global methods from module Kernel
, BasicObject
, and Object
are allowed generally to omit parenthesis if readability permits.
It has also been proposed that private method calls of classes Module
and Class
, from within a module or class definition where the receiver is self
or implied to be the module or class itself, can also omit parenthesis.
(This rule is referred to as “methods having keyword status” [reserved word == keyword].)
However, in my opinion, you should never be ridiculed for using parenthesis even when allowed to omit them. (The foremost Ruby coding style guide will take the opposite tack and will produce rule violations if parenthesis are used when they can be omitted.)
At the module level, insert a blank line before blocks like this and methods (or other blocks.)
… spacing for readability is paramount for argument lists, arrays, hashes and expressions.
(Arrays or argument lists of numbers tend to blur together at the end of a long day.) Ie:
defaults = [5.0, 0.5, "No", 255.0, 255.0, 255.0]
As said above … You do not need to require
dependent files, if this file does not use them.
And since SketchUp 2014, the SketchUp load cycle will load the 3 files from it’s "Tools"
subfolder, and your code does not really need to require
them anyway. IE, the calls …
require 'sketchup.rb'
require 'extensions.rb'
… are not explicitly needed because they will always already be loaded by the time your module is being loaded, but more so because this file doesn’t call any feature defined by those files.
And, … the code that was in the body of the init()
method, again is not being used, so delete …
#Loading in handles
model = Sketchup.active_model
selection = Sketchup.active_model.selection
entities = Sketchup.active_model.entities
materials = Sketchup.active_model.materials
layer_array = Sketchup.active_model.layers
Local variables at the module level are unusable by the module’s methods anyway.
They’d need to be @@vars
or @vars
in order for any of the methods to access them.
But such assignments would need to be dynamic, because users close models and open other models. So assigning a set of “@handles” would be done at the last second, probably as the first step in a command, so that your code is sure that the handles are for objects in the active model.
A note about entities. Always prefer to work with the active_entities rather than assume the user always wants to work at the top level of the model. Ie, instead of …
@entities = Sketchup.active_model.entities
… use …
@entities = Sketchup.active_model.active_entities
… should be …
result = UI.messagebox(message, MB_OKCANCEL)
return if result == IDCANCEL
… or even better …
result = UI.messagebox(message, MB_OKCANCEL)
return unless result == IDOK
This is called a statement with a conditional expression in modifier position.
Note also that the return
statement can have an expression to return immediately following the return
. Ex:
result = UI.messagebox(message, MB_OKCANCEL)
return false unless result == IDOK
As said above, don’t change the USER’s active layer/tag. Ruby API code doesn’t need to do this anyway as code would likely always be creating geometry inside a group or component’s definition’s entities collection (associated with “Layer0”/“Untagged”,) then creating an instance in the model and afterward setting the instance’s layer/tag.
Besides this workflow, changing the active Layer/Tag will fire off any LayersObserver
objects that are active by other extensions. This is not something your code needs to have happening.
In addition the 3 local variable assignments in the method are unneeded of do nothing. This method is really a one-liner …
# Create (if necessary) and return a reference to the "Trim" layer/tag.
def create_trim_layer(model, tagname = 'Trim')
model.layers[tagname] || model.layers.add(tagname)
end
… this above example allows localized language layer/tag names to be passed into the method.
(Not all users run SketchUp in English.)