SketchUp Ruby API site updated for SketchUp 7.1

Monday, November 2, 2009

SketchUp 7.1 launched back in September. Last week, we updated the SketchUp ruby API Site with all the changes that have been made since the release of Sketchup 7.0 (Nov 2008). This includes documentation fixes and updates for several new methods. For all the details check out the API release notes.

We would also like to say "Thanks!!" to the SketchUcation community and the guys over at Smustard.com for sending us Ruby documentation fixes. SketchUp's amazing developer community continues to be one of the best "features" of the Ruby API.

~Bryce Stout
Product Manager

Dynamic Components that react to scene (aka page) changes

Thursday, July 23, 2009

Have you ever wanted to have a Dynamic Component that reacts to Scene changes?



Well, this blogpost tells you how to write one (thanks to John Clemens for the idea).



First of all, you need a mechanism to detect a scene change. After some struggles with using the PagesObserver, I decided to use the FrameChangeObserver.



The add_frame_change_observer method is used to add a new FrameChangeObserver that is called with each frame of an animation. This means the end user has clicked on a Scene tab (aka Page) inside SketchUp and the camera is animating to that scene. Moreover, it provides you with a percent_done variable and fromPage and toPage variables which allow you to have finer feedback over the state of the animation.



The other challenge is that we need to find the DC that will react to the scene change. This can be accomplished by traversing the model entities and examining all the component instances that have a “dynamic_attributes” dictionary attached to them; those are the dynamic components in the model. Once we have a way to find all the dynamic components, it is just a matter of detecting which ones we want to influence on a scene change. For that, we add a SCENE_CHANGE Boolean (true or false values only) attribute to the top level attributes of our DC to indicate whether or not we want that DC to change. Then, we add a SCENE_TRIGGER attribute that contains an index that is affected by each scene change and that changes between 1 and 4 (this can be customized to be anything you want). This can then can be used to drive changes in the DC.



To see this in action, put the attached script in the Tools or Plugins folder of SketchUp, open the attached model, change the scene, and ta-dah the magic begins. You will notice that one of the two components does not change, the reason being that the attribute SCENE_CHANGE for that component is set to 'false'. Change it to 'true' and you will see it changing.



It is important to notice that there are several observers available from Ruby (e.g ToolsObserver, SelectionObserver, etc.) and any of them can be used to trigger changes in dynamic components using the same strategy I used in the attached script. DC can be made to change according to which tool is currently active, or depending on the currently selected material.


Download the example model here.



Code snippet below

----------------------------------------------------------------

# Copyright 2005-2008, Google, Inc.

# 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.
#

# This code snippets allows you to have Dynamic Components
# that modify themselves based on a Scene Change
#
# They need to have at the top level the following attributes
# "scene_change" a boolean value that if true force the DC to react to scene changes
# and if false has the DC ignore the scene change
# and
# "scene_trigger" which is the index that is incremented every time there is a scene change and
# it is used to change attributes for the DC.
#
#
# Author: Simone Nicolo

require 'sketchup.rb'


# Define the Frame Change Observer which is documented in the Pages class.
class FrameChangeObserver

def initialize
# @current page is used to make sure that the observer is only triggered by page changes
# This is to workaround the PC bug that does not correctly populate the fromPage variable in the
# callback to frameChange.
@current_page = Sketchup.active_model.pages.selected_page
end

# Callback triggered by the Frame change observer.
# fromPage is populated with the page we were coming from, toPage is
# populated with the page we were transitioning to
# and percent done contains the percentage of the transition that
# has completed.
def frameChange(fromPage, toPage, percent_done)
# If there has been a page change and it has completed
if (not toPage.nil? and percent_done == 1) then
if @current_page != toPage then
#update the current page
@current_page = toPage
#find the DCs that need redrawing, and redraw them.
find_dc_for_redraw
end
end
end

# This function finds all the DC's that need to be redrawn based on a scene transition and
# on the boolean scene_change attribute if scene_change is true, change and redraw
# the DC on a scene transition, if it is false do not change or redraw the DC.
def find_dc_for_redraw
entities_list = Sketchup.active_model.entities
if entities_list != []
# For all the entities in the model
entities_list.each do |item|
type = item.typename
case type
# Find the Dynamic Component instances
when "ComponentInstance"
if not item.attribute_dictionaries.nil?
attributes = item.attribute_dictionaries[DICTIONARY_NAME]
# If they have a scene_change attribute that contains true
if (not attributes.nil? and attributes[SCENE_CHANGE] == 1.to_s)
# Increment the scene_trigger attribute.
# Here we are allowing the scene trigger attributes to only have the values
# 1, 2, 3, and 4.
attributes[SCENE_TRIGGER] = (((attributes[SCENE_TRIGGER].to_i+1)%5)).to_s
if attributes[SCENE_TRIGGER] == "0" then
attributes[SCENE_TRIGGER] = 1.to_s
end
# Redraw the DC using the $dc_observers global variable.
$dc_observers.get_latest_class.redraw_with_undo(item)
end
end
end
end
else
UI.messagebox("There are no entities in this model!")
end
end

end

if not file_loaded? 'scenes.rb'
# Useful constants
# Dynamic Components dictionary name
DICTIONARY_NAME = "dynamic_attributes"
# Boolean attribute that turns on and off the reaction to a scene change for the DC
SCENE_CHANGE = "scene_change"
# Index attribute for the DC that is used to drive changes in the DC.
SCENE_TRIGGER = "scene_trigger"
# Attach the frame chage observer to the global Pages object.
id = Sketchup::Pages.add_frame_change_observer(FrameChangeObserver.new)
file_loaded 'scenes.rb'
end


Champion the 3D Web using COLLADA Contest

Thursday, May 7, 2009

Our friends over at the Khronos group are holding a Champion the 3D Web using COLLADA Contest. They're looking for the best 3D models, avatars, worlds, or simulations created using COLLADA. Prizes include cash awards and opportunities at a future Immersive Education Initiative in-world (virtual) events. The contest will run through July 15, 2009. Winners will be announced at Siggraph this year in New Orleans. The contest is sponsored by the Khronos Group and the Media Grid Immersive Education Initiative. Please check out the contest site for full details.

Using SketchUp to enter the contest

SketchUp can be used to create models for this contest. SketchUp Pro and the Pro Evaluation both export COLLADA (DAE) files directly. The free version of SketchUp can export Google Earth (KMZ) files which contain a COLLADA model. To extract a COLLADA model from a KMZ file follow these steps:

  1. Export your SketchUp model to KMZ.
  2. Change the file extension from ".kmz" to ".zip".
  3. Double-click on the ZIP file.
  4. Double-click on the "models" folder.
  5. Locate the DAE file. Copy and paste this file to your Desktop.
  6. Check your file's appearance in a COLLADA viewer like Swirl X3D Viewer©
  7. Submit your model to the contest.

Good Luck!
Bryce Stout
Product Manager
Google

Ruby API Objects diagram

Friday, April 17, 2009

Have you ever wondered how the various classes are organized in the ruby API?
Well, I recently created a diagram to show the objects hierarchy for the SketchUp Ruby API.

It can be found here and it provides an easy way to visualize relationships between SU API's various objects.

Below are some examples of ways in which the diagram can be used:

Example 1
In the diagram, it is evident that the Entity class has several subclasses which all inherit the Entity class methods. For example, the Face class can add an EntityObserver to its instances just by taking advantage of the inherited methods from the Entity class.

Example 2
It is also visually clear that I can get handles to different entities using Ruby. For example, if I want to obtain a handle to my current model, I can do so using Sketchup.active_model or Entity.model. The Entity.model method will work on any class that inherits from Entity.

Example 3
I can clearly visualize which classes have observers and which do not. For example, I can easily see that the View class has its own observer, while the camera class does not.

To keep it as simple as possible, some classes were not included in the diagram.

Hopefully this will help you better visualize the organization of the Ruby API elements.

Simone.

SketchUp API Updates

Wednesday, April 1, 2009

Updated Ruby API Documentation

The guys over at the SketchUcation Community Forums have been submitting documentation corrections to us and we just pushed a bunch of updates to our Ruby API Site . Thanks to everyone who's been helping us improve our docs! If you find other typos or problems please join the thread on SketchUcation or post to our ruby group.

New Ruby Tutorial

Check out our new tutorial on Attribute Reporting to learn how to read attribute data from entities in your models.

New SDK Package

We recently repackaged the SDK to make it a bit easier to use. Improvements include:

- Reorganized folders to make them more understandable
- New GettingStarted.html intro to help get folks headed down the right path
- Updated SkpReader and SkpWriter documentation
- Updated SkpToXml example to compile on the PC

We also removed Client Warehouse SDK from the package. If you need support on this please post your contact info to the SDK support group.

There were no changes to the headers or binaries so there are no code fixes with this release. Please download the latest package at - http://code.google.com/apis/sketchup/docs/downloadsdksubmit.html

Bryce Stout
Product Manager
Google

Deleting Unnecessary Edges

Thursday, March 12, 2009



Have you ever opened a SketchUp file that you didn’t create, or imported a model from another application, and found that it has hundreds of needless hidden or softened lines separating coplanar faces? Wish you could get rid of them with a simple ruby script? Now you can!

First, set up a few variables:

def variables
@group_list = []
@component_list = []
@delete_these = []
@model_list = Sketchup.active_model.entities
end

Then write a method that determines if an edge is 1) hidden or softened, and 2) bordered by two co-planar faces. If the edge meets each of those requirements, it is added to @delete_these list.

def delete_soft_lines(e)
# Determine if the edge is soft or hidden, and if it is bordered by
# two faces.

if ((e.soft? and e.faces.size == 2) or (e.hidden? and e.faces.size == 2))
# Determine if the two bordering faces are coplanar.
# Note: This will only catch cases where the front faces are pointing
# in the same direction.

if (e.faces[0].normal.dot(e.faces[1].normal) > 0.999999999)
# Add the edge to the list of entities that will be deleted.
@delete_these.push(e)
end
end
end

Now create a method to find all the edges in the model, including edges that are embedded in groups and components:

def traverse_model_entities
# @model_list will not be empty until all of the groups, components
# and edges have been traversed.

while @model_list != []
@model_list.each do |item|
# Determine if the entity is an edge, group, or component.
type = item.typename
case type
when "Edge"
# Determine if the edge should be deleted, and add it to the
# @delete_these list.

delete_soft_lines(item)
when "Group"
# Add each of the entities in the group into the group_list
# array.

item.entities.each do |entity|
@group_list.push entity
end
when "ComponentInstance"
# .entities can be called on a Component Definition, but not on
# a Component Instance.
# Use the .definition method to access the Component Definition
# that the instance belongs to.

item.definition.entities.each do |entity|
# Add each of the entities in the component into the
# component_list array.

@component_list.push entity
end
end
end
# Update the @model_list so it contains only the entities that were
# embedded in groups or components. These entities haven't been
# counted yet.

@model_list = @group_list + @component_list
# Clear out the group and component lists so they're ready for the
# next level of sub-groups/components.

@group_list = []
@component_list =[]
end
# The edges that need to be deleted have all been added to the
# @delete_these list, so erase those entities from the
# active SketchUp model.

Sketchup.active_model.entities.erase_entities(@delete_these)
end


Finally, add a “Delete unnecessary edges” item to Tools menu, and pop a dialog letting you know the edges were deleted:
def delete_edges
variables
traverse_model_entities
UI.messagebox("Edges deleted.")
end

UI.menu("Tools").add_item("Delete unnecessary edges") {delete_edges}

Google I/O 2009

Friday, March 6, 2009



The Google I/O Developer Conference is May 28th and 27th in San Francisco, CA. Members of the SketchUp team will be presenting in the What you don't know about Geo APIs can't hurt you session and will also be available during Office Hours. Hope to see you there!


Posted by Bryce Stout, Product Manager