Just like everyone else these days I have been trying out OpenAI’s ChatGPT and Microsoft’s Bing (which now also uses the same AI – artificial intelligence – engine). One of my lines of exploration (beyond “what does this thing know about me?”) was the question whether it can auto-generate SketchUp Ruby code with AI. And – as it turns out – it can do that. Albeit with some caveats.

As a start, you can go to ChatGPT and (after signing up) ask a reasonably specific question like “Which Ruby code would draw a box in SketchUp?”. That would spit out somewhat appropriate code that you can just paste into e.g. the Ruby Code Editor. Apparently the SketchUp API is part of its knowledge base and generating SketchUp-specific Ruby code is thus well within its grasp. You could even refine the query with a comment like “Isn’t there a simpler way to do this?” and get a different approach. However, when I teased the engine with “Can this be done in a single line?” it spit out the following smart-assy response:

Sketchup.active_model.entities.add_3d_text("Box", TextAlignCenter, "Arial", false, false, 1.0, 0.0, nil, false, 0.0, 0.0, 1.0, 1.0, true, 0.5, 0.0, true).explode

So, as you can see this is certainly a way to get a “box” drawn in SketchUp. But that’s not really what I meant. And there are errors in that code. Oh, well…

Let’s try a different approach that would be more integrated with SketchUp. For this, I used the code that is included at the end of this article to query the OpenAI API directly from SketchUp’s Ruby interface. The following code (and all comments!) was then generated using the “text-davinci-003” AI model from OpenAI when given the prompt “Draw a box whose side lengths are 2 feet.”

# Create a new SketchUp model
model = Sketchup.active_model

# Start an undo section
model.start_operation("Draw a Box", true)

# Create an array of points for the box
points = [
  Geom::Point3d.new(0, 0, 0),
  Geom::Point3d.new(2, 0, 0),
  Geom::Point3d.new(2, 2, 0),
  Geom::Point3d.new(0, 2, 0)
]

# Create the face from the points
face = model.entities.add_face(points)

# Push the face back 2 feet
face.pushpull(-2)

# End the undo section
model.commit_operation

# Report success
UI.messagebox("Box drawn successfully!")

This is pretty astonishing! For one, I got fully commented and syntax-accurate code. And it generated a box, as I had asked. And best of all, it included an undo wrapper and a success message – two things I hadn’t even asked for! There were some quirks, however. The first one appears in the first line where the code claims to create a new model. That is not true – we are simply getting a handle to the currently active model. Also, the dimensions were not entered as 2.feet and therefore defaulted to two inches. So, while I did get a box, there were some errors.

I tried a few other prompts with varying success. Below are two examples that worked. Other attempts simply failed either with a syntax error or some other issue.

The following (rather failed) attempt shown below resulted from the prompt “Draw a house”. I increased the “temperature” value to 0.2 to get some variation and among the solutions that it generated, the one shown below might be the most architectural sample, despite being a bit deconstructivist in nature.

Calling OpenAI’s API from SketchUp’s Ruby

Want to try this with an extension instead? I just packaged the code in an experimental extension that you can download from here.

So, how did I set up SketchUp to generate this code and then create the box that you see in the image at the top of this post? You can try it for yourself: Once you sign up with OpenAI and get yourself an API key, paste the following code into the Ruby Code Editor, add your own API key, and then hit Run!

require 'net/http'
require 'uri'
require 'json'

# Set the endpoint and API key for the OpenAI API
endpoint = "https://api.openai.com/v1/completions"
api_key = "<YOUR API KEY>"

# Set up the prompt for the code completion
prompt = "Use SketchUp Ruby\n"

# Pick one of these options or write your own
prompt += "Draw a box whose side lengths are 2 feet"
# prompt += "Draw 20 lines of random orientation and length"

# Set up the HTTP request with the API key and prompt
uri = URI(endpoint)
req = Net::HTTP::Post.new(uri)
req["Content-Type"] = "application/json"
req["Authorization"] = "Bearer #{api_key}"
req.body = JSON.dump({
  "model" => "text-davinci-003",
  "prompt" => prompt,
  "max_tokens" => 256,
  "top_p" => 1,
  "n" => 1,
  "temperature" => 0,
  "stream" => false
})

# Make the HTTP request to the OpenAI API and parse the response
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(req)
end
response_body = JSON.parse(res.body)

# Get the generated code from the API response
generated_code = response_body["choices"][0]["text"]

# Display the generated code in the Ruby console
puts generated_code

# Run the generated code - fingers crossed!
eval generated_code

The code is pretty self-explanatory (especially after reading Chapter 7). Note that I added “Use SketchUp Ruby” to each prompt so that I wouldn’t get an unusable answer (i.e. in another language). What turns the generated code into actual geometry is the last line where an eval command executes the generated code. This is – of course – also where many errors happen because anything that is not properly formatted as Ruby code will trip Ruby up at this point (hence the crossed fingers).

In terms of customizations, you can try various language models. I tried OpenAIs code-completion models (code-cushman-001 and code-davinci-002) but have found that answers generated by text-davinci-003 seem to work best. Furthermore, you can try increasing the “temperature” parameter, which controls what could be called “creativity” in the generated answer. Having zero as a value there will always produce the same answer, but increasing that too far may generate weird and unusable answers.

My Thoughts about This

Adding AI to a software like SketchUp can open up interesting prospects. It would be nice if we could just ask for a particular piece of geometry and then have it appear. In practice, however, there are so many modeling aspects (e.g. start points, combined geometry, inferences,…) that would require quite a few instructions and will likely make it easier to just hand-model something.

Nevertheless, the code presented here might be a good starting point for further refinement. After all, every AI implementation needs added guardrails, or helping hands to become fully useful. Having AI generate a somewhat “creative” solution can also sometimes be a good starting point. For example, most SketchUp code that the AI engine has generated this way had errors in it and required at least a bit of tweaking. But it got me closer to the actual solution, which can indeed be a time-saver.

So, let’s keep exploring and see how powerful this becomes at some point.

Links

P.S. This Works, Too

I’ll keep adding more here as I keep finding prompts that work…

Comments and Reactions