Skip to content

Instantly share code, notes, and snippets.

@benlangfeld
Created June 3, 2011 13:23
Show Gist options
  • Save benlangfeld/1006331 to your computer and use it in GitHub Desktop.
Save benlangfeld/1006331 to your computer and use it in GitHub Desktop.
# Copyright 2004-2005, @Last Software, Inc.
# This software is provided as an example of using the Ruby interface
# to SketchUp.
# Permission to use, copy, modify, and distribute this software for
# any purpose and without fee is hereby granted, provided that the above
# copyright notice appear in all copies.
# THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#-----------------------------------------------------------------------------
# Name : Parametric 1.0
# Description : This file defines the Parametric module that lets you define parametric
# objects that you can edit.
# Menu Item : NONE
# Context Menu: NONE
# Usage : N/A
# Date : 9/10/2004
# Type : Tool
#-----------------------------------------------------------------------------
# This file defines the Parametric module that lets you define parametric
# objects that you can edit.
require 'sketchup.rb'
#=============================================================================
class Parametric
# Initialize a newly created instance of the object
def initialize(*args)
data = args[0]
data = self.prompt("Create") if not data
return if not data
if( data.kind_of? Sketchup::Entity )
@entity = data
else
return if not validate_parameters(data)
model = Sketchup.active_model
model.start_operation self.class.name
self.create_entity(model)
container = self.get_container
if not container
model.abort_operation
return
end
self.create_entities(data, container)
# Set the parameters for the object
self.set_attributes(data)
# Apply the transform if one was given
t = args[1]
if( t.kind_of? Geom::Transformation )
@entity.transformation = t
end
model.commit_operation
@entity
end
end
# The name to give to the new object
def compute_name
self.class.name
end
# Create a new parametric Entity. The default implementation creates
# a Group. Derived classes can over-ride this method to create a
# ComponentDefinition or ComponentInstance instead.
def create_entity(model)
@entity = model.active_entities.add_group
end
# Get the container in which to add new Entities.
# This method should not need to be over-ridden by derived classes
def get_container
if( @entity.kind_of? Sketchup::Group )
container = @entity.entities
elsif( @entity.kind_of? Sketchup::ComponentInstance )
container = @entity.definition.entities
elsif( @entity.kind_of? Sketchup::ComponentDefinition )
container = @entity.entities
else
container = nil
end
container
end
# Get the attribute dictionary
def Parametric.attribute_holder(entity)
if( entity.kind_of? Sketchup::Group )
return entity
elsif( entity.kind_of? Sketchup::ComponentInstance )
return entity.definition
elsif( entity.kind_of? Sketchup::ComponentDefinition )
return entity
end
nil
end
def attribute_dictionary(create_if_needed = false)
attrib_holder = Parametric.attribute_holder(@entity)
attrib_holder ? attrib_holder.attribute_dictionary("skpp", create_if_needed) : nil
end
# Get the parameter data from an entity
def parameters
return nil if not @entity
attribs = self.attribute_dictionary
return nil if not attribs
data = {}
attribs.each do |key, value|
if( key != "class" )
data[key] = value
end
end
data
end
# Show a dialog and get the values from the user
# TODO: The data variable is a Hash of values to get from the user.
# Because it is a Hash, the order is not specified. I really need some way
# for the derived classes to control the order that the parameters are
# displayed to the user.
def prompt(operation)
# get the parameters
if( @entity )
data = self.parameters
else
data = self.default_parameters
end
if( not data )
puts "No parameters attached to the entity"
return nil
end
title = operation + " " + self.class.name
keys = []
prompts = []
values = []
data.each do |key, value|
if( key != "class" )
keys.push key
prompts.push self.translate_key(key)
values.push value
end
end
results = inputbox( prompts, values, title )
return nil if not results
# Store the results back into data
# results will be an Array with one value for each prmopt
results.each_index { |i| data[keys[i]] = results[i] }
data
end
# Attach attributes to the object
def set_attributes(data)
# Get the AttributeDictionary - create it if needed
attribs = attribute_dictionary(true)
# Set the class name
attribs["class"] = self.class.name
# now set the data values
data.each { |key, value| attribs[key] = value }
attribs
end
def entity
@entity
end
# Edit the parameteric object. This will prompt for the new values
# and then regenerate the geometry
def edit
if( not @entity )
puts "There is no Entity to Edit"
return false
end
data = self.prompt "Edit"
return false if not data
# Make sure that valid values were entered
ok = self.validate_parameters(data)
if( not ok )
return false
end
# Now clear the old definition and regen the entities
container = self.get_container
model = @entity.model
model.start_operation "Edit " + self.class.name
container.clear!
self.create_entities(data, container)
self.set_attributes(data)
model.commit_operation
@entity
end
#-----------------------------------------------------------------------------
# Class methods for editing parameteric objects
# Determine the class of a parametric entity
def Parametric.get_class(ent)
attrib_holder = Parametric.attribute_holder(ent)
return nil if not attrib_holder
attrib_holder.get_attribute "skpp", "class"
end
# Determine if an Entity is a parametric object
def Parametric.parametric?(ent)
klass = Parametric.get_class(ent)
return false if not klass
# Make sure that we can actually create an instance of this class.
begin
new_method = eval "#{klass}.method :new"
rescue
# If we couldn't find the new method, it probably means that
# the code for this kind of parametric object wasn't loaded
puts "Could not find implementation of #{klass}"
return false
end
# return the class name
klass
end
def Parametric.selection_parametric?
ss = Sketchup.active_model.selection
false if ss.count != 1
Parametric.parametric? ss.first
end
def Parametric.edit(ent)
if( not Parametric.parametric?(ent) )
UI.beep
puts "#{ent} is not a parametric Entity"
return false
end
# Get the class of the parametric object
klass = Parametric.get_class(ent)
# Create a new parametric object of that class
new_method = eval "#{klass}.method :new"
obj = new_method.call ent
if not obj
puts "Could not create the parametric object for #{klass}"
return false
end
# Now edit the object
obj.edit
end
# Edit the current selection
def Parametric.edit_selection
if not Parametric.selection_parametric?
UI.beep
puts "The selected Entity is not parametric"
return false
end
Parametric.edit Sketchup.active_model.selection.first
end
#-----------------------------------------------------------------------------
# The following methods should be implemented by derived classes. Some of them
# are required, and some are only optional
# create_entities is called to create the entities for the parametric object.
# the parameters needed to create the object are passed in as a Hash.
# This must be implemented by any class that includes Parametric
def create_entities(data, container)
puts "create_entities must be implemented by #{self.class.name}"
end
# Get the default parameters for the object.
def default_parameters
puts "default_parameters must be implemented by #{self.class.name}"
end
# Check that valid parameters were entered
# return true if the parameters are OK or false if they are not
def validate_parameters(data)
true
end
# This allows the object to translate the keys used to store the parameters
# into different prompts to display in the UI. If not implemented, the
# parameter keys will be used for the prompts
def translate_key(key)
key
end
end
#=============================================================================
# Add a context menu handler that will add a menu choice to a context menu
# for editing parametric objects
if( not $parametric_loaded )
UI.add_context_menu_handler do |menu|
klass = Parametric.selection_parametric?
if( klass )
menu.add_separator
menu.add_item("Edit #{klass}") { Parametric.edit_selection }
end
end
$parametric_loaded = true
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment