Skip to content
June 17, 2011 / stevebaer

Kinect SDK

YES!!! This could lead to some incredible applications.

http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/

Now if they only had a version that clipped to your monitor and tracked your fingers…

June 2, 2011 / stevebaer

Outside the Box 2 – Calling the Shapeways API from Python

I received a question on accessing the ShapeWays API from my last “Outside the Box” blog and figured I should dig in and figure out what is going on. Shapeways has a web service API that uses SOAP and a WSDL page (http://en.wikipedia.org/wiki/Web_Services_Description_Language) as described here
http://www.shapeways.com/api

This API is a bit different than the REST API which I wrote a script to access in my previous blog post. In order to use this API, I ended up modifying a script originally put together by Michael Foord (author of “IronPython in Action”) that creates a .NET assembly on the fly for a given WSDL url.

The two scripts are in gists at the end of this post

Place both of these scripts in the same directory and open the “useshapeways.py” script. Here’s the script itself

"""Sample script that accesses the shapeways API
[url]http://www.shapeways.com/api[/url]
"""
import wsdlprovider

wsdl_url = "http://api.shapeways.com/v1/wsdl.php"
username = "username"
password = "password"
application_id = "rhinotest"

assembly = wsdlprovider.GetWebservice(wsdl_url)
shapeways = assembly.SWwsdlService()
session_id = shapeways.login(username, password, application_id)
if session_id:
    #get list of printers available
    printers = shapeways.getPrinters(session_id, "", application_id)
    if printers:
        for printer in printers:
            print "printer:", printer.title
            for material in printer.materials:
                print " - material ", material.title

The script uses the wsdlprovider script to generate a .NET assembly from the shapeways wsdl url. This assembly has a class in it called SWwsdlService which we create an instance of and call functions on. It looks like a normal class to python, but all of the function calls are sent to ShapeWays over the internet and response are turned into classes that you can use. This sample simply logs into shapeways to get a “session id” and then asks Shapeways for a list of it’s available printers along with what materials each printer supports.

At the time of this blog post, the printed output from this script is

printer: Somatech FDM
- material Grey Robust
printer: Somatech Objet 720
- material Black Detail
- material White Detail
- material Transparent Detail
printer: SLS Printer
- material White Strong & Flexible
printer: Metal Printer matt
- material Gold Plated Glossy
- material Antique Bronze Glossy
- material Antique Bronze Matte
- material Stainless Steel
printer: SLS Color Printer
- material Black Strong & Flexible
printer: Silver Printer
- material Silver Glossy
- material Silver
printer: ZPrinter 650
- material Sandstone
- material Full Color Sandstone
printer: SLS Alumide
- material Alumide
printer: Glass Printer
- material High Gloss Black Glass
- material High Gloss White Glass
- material Milky White Matte Glass
printer: Metal printer Gold
- material Gold Plated Matte
printer: SLS Color Printer New
- material Dark Grey Strong and Flexible
- material Indigo Strong and Flexible
- material Winter Red Strong and Flexible
printer: HD printer
- material Frosted Detail
printer: UHD printer
- material Frosted Ultra Detail
printer: SLS Printer polished
- material White Strong & Flexible Polished
printer: Ceramics printer
- material Glazed Ceramics

Pretty neat!

"""Sample script that accesses the shapeways API
http://www.shapeways.com/api
"""
import wsdlprovider
wsdl_url = "http://api.shapeways.com/v1/wsdl.php"
username = "username"
password = "password"
application_id = "rhinotest"
assembly = wsdlprovider.GetWebservice(wsdl_url)
shapeways = assembly.SWwsdlService()
session_id = shapeways.login(username, password, application_id)
if session_id:
#get list of printers available
printers = shapeways.getPrinters(session_id, "", application_id)
if printers:
for printer in printers:
print "printer:", printer.title
for material in printer.materials:
print " – material ", material.title

view raw
useshapeways.py
hosted with ❤ by GitHub

'''
Generate a proxy class for a SOAP web service from its WSDL.
Based on C# implementation from the DynamicWebService IronPython example.
'''
# Original script from "IronPython in Action" Book code found at
# http://code.google.com/p/ironpython/
# http://www.voidspace.org.uk/ironpython/
# 2 June 2011 – S. Baer
# Modified to cache the generated assemblies for performance purposes
import clr
clr.AddReference("System.Web.Services")
clr.AddReference("System.Xml")
from System.Web.Services.Description import (
ServiceDescription, ServiceDescriptionImporter
)
from System.Web.Services.Protocols import SoapHttpClientProtocol
from System.IO import MemoryStream
from System.Net import WebClient
from System.CodeDom import (
CodeCompileUnit, CodeNamespace
)
from System.CodeDom.Compiler import CodeDomProvider, CompilerParameters
from System.Xml.Serialization import CodeGenerationOptions
def GetBytes(url):
'download the file at url'
return WebClient().DownloadData(url)
def CreateWebServiceFromWsdl(wsdl):
'convert the WSDL into an assembly containing the web service proxy classes'
# generate codeDom from wsdl
sd = ServiceDescription.Read(MemoryStream(wsdl))
importer = ServiceDescriptionImporter()
importer.ServiceDescriptions.Add(sd)
codeCompileUnit = CodeCompileUnit()
codeNamespace = CodeNamespace("")
codeCompileUnit.Namespaces.Add(codeNamespace)
importer.CodeGenerationOptions = (CodeGenerationOptions.GenerateNewAsync
| CodeGenerationOptions.GenerateOldAsync)
importer.Import(codeNamespace, codeCompileUnit)
# compile CodeDom into an assembly
provider = CodeDomProvider.CreateProvider("CS")
compilerParams = CompilerParameters()
compilerParams.GenerateInMemory = True
compilerParams.IncludeDebugInformation = False
results = provider.CompileAssemblyFromDom(compilerParams, codeCompileUnit)
generatedAssembly = results.CompiledAssembly
return generatedAssembly
# dictionary used to cache assemblies generated from wsdl
__assembly_cache = {}
def GetWebservice(url):
key = url.lower()
if not __assembly_cache.has_key(key):
'download the WSDL for the service URL and generate an assembly from it'
if url.lower().endswith(".asmx"): url += "?WSDL"
data = GetBytes(url)
assembly = CreateWebServiceFromWsdl(data)
if not assembly: return None
__assembly_cache[key] = assembly
return __assembly_cache[key]
def FindProxyType(assembly):
"""if you aren't sure of the name of the proxy type that will be generated,
use this to find it"""
for name in dir(assembly):
attr = getattr(assembly, name)
if type(attr) is type and issubclass(attr, SoapHttpClientProtocol):
return attr

view raw
wsdlprovider.py
hosted with ❤ by GitHub

June 1, 2011 / stevebaer

Multithreaded Python

multithreaded_py[
One of the nice bits that we have access to in Rhino python is the Task Parallel Library that is built into .NET 4
http://msdn.microsoft.com/en-us/library/system.threading.tasks.aspx

This set of classes and functions makes it relatively easy to write things like parallel for loops in which every iteration of the loop may be processed on different threads. This nice thing about parallel for loops is that they make coding with multiple threads much simpler since the “multi-threading” only occurs inside the for loop and once the loop is finished you know that the all of the threads have completed and you are back on the main execution thread.

Here’s a sample python script which runs many Plane-Brep intersections either on a single thread or using multiple threads.

import System.Threading.Tasks as tasks
import Rhino
import rhinoscriptsyntax as rs
import time, math
import scriptcontext
def radial_contour(brep, parallel, slice_count=360):
"""Generate series of curve slices through a brep by rotating a plane
multiple times and intersecting that plane with the brep. This function
demonstrates the use of .NET Parallel.For in order to run the function
in parallel
Parameters:
brep = the Brep to contour
parallel = If True, this function will compute intersections in multiple
threads using Parallel.For. If False, all intersections will be performed
on a single thread
slice_count = number of slices to generate. Slices are evenly distributed
over a full circle
"""
if not brep: return
results = range(slice_count)
rotation_axis = Rhino.Geometry.Vector3d(0,1,0)
intersect_tol = scriptcontext.doc.ModelAbsoluteTolerance
# local function that does the intersection work. This function is called
# once for each angle in "slice_count" and needs to be thread-safe
def slice_brep_at_angle(i):
try:
angle_rad = i/slice_count * 2.0 * math.pi
plane = Rhino.Geometry.Plane.WorldXY
plane.Rotate(angle_rad, rotation_axis, Rhino.Geometry.Point3d.Origin)
rc, crvs, pts = Rhino.Geometry.Intersect.Intersection.BrepPlane(brep, plane, intersect_tol)
if rc: results[i] = crvs
else: results[i] = None
except:
pass
if parallel:
tasks.Parallel.ForEach(xrange(slice_count), slice_brep_at_angle)
else:
for i in xrange(slice_count): slice_brep_at_angle(i)
return results
if __name__=="__main__":
brep = rs.GetObject("Select Brep", rs.filter.polysurface)
brep = rs.coercebrep(brep)
if brep:
# Make sure the Brep is not under the control of the document. This is
# just done so we know we have a quick to access local copy of the brep
# and nothing else can interfere while performing calculations
brep.EnsurePrivateCopy()
#run the function on a sinlge thread
start = time.time()
slices1 = radial_contour(brep, False)
end = time.time()
print "serial = ", endstart
#run the function on mulitple threads
start = time.time()
slices2 = radial_contour(brep, True)
end = time.time()
print "parallel = ", endstart
if slices2:
for curveset in slices2:
if curveset:
for curve in curveset: scriptcontext.doc.Objects.AddCurve(curve)
scriptcontext.doc.Views.Redraw()

view raw
radial_contour.py
hosted with ❤ by GitHub

May 27, 2011 / stevebaer

Outside the Box – Using Web Services from Python

gtranslate_ghx
I’ve been trying to learn more about things like web services and APIs provided by internet companies (gotta figure out what all of these buzzwords are.) One thing I’ve noticed is that many companies now provide a REST API which return JSON objects. There are enough resources on the web that describe REST and JSON that I don’t need to repeat it here; I’m just going to get to my python experiments on this technology.

So what does it do?
The python script uses the Google Translate API to translate text from one language to another. Maybe not the most useful new tool for Rhino, but it shows how simple it is to format a request to a web service and get at the results.

Some important things to note:

  1. The scripts in both attachments are exactly the same. There is code in the script to figure out if it is running as a normal Rhino python script or inside of Grasshopper.
  2. The script does different things when it is running in Rhino versus when running in Grasshopper. In Rhino, the script converts text dot text which in Grasshopper the script uses input and output variables.
  3. I’m using the sticky variable to cache results. This way we don’t have to keep going out to Google if we are translating the same text over and over again

Search around on the internet for REST apis that return JSON data. Your python scripts that use these other services will end up looking pretty similar to the samples I posted (unless you don’t like my scripting style;)) Here are some interesting APIs to look at:
http://code.google.com/more/
http://friendfeed.com/api/documentation
http://www.flickr.com/services/api/
http://developer.ning.com/docs/ningapi/1.0/index.html
http://develop.github.com/
http://api.pachube.com/

"""Use Google translate web service"""
import rhinoscriptsyntax as rs
import json, urllib
import scriptcontext
# Api key that I (Steve Baer) got from Google
# http://code.google.com/apis/console-help/#UsingKeys
# You might want to generate your own key, but I don't care
# if you continue to use this one.
KEY = "AIzaSyAZGoS-GjZGaSHZMZdoczfdUtWTjm_D-p4"
def translate(text, source="en", target="fr"):
"""Translate text from one language to another. Returns the
translated text on success or None on failure
"""
# see if we already have a cached answer
cache_dict_name = "translate " + source + ":" + target
if scriptcontext.sticky.has_key(cache_dict_name):
cache = scriptcontext.sticky[cache_dict_name]
if cache.has_key(text): return cache[text]
url = "https://www.googleapis.com/language/translate/v2"
url += "?key=" + KEY
url += "&q="+urllib.quote(text.encode('utf-8'))
url += "&source="+source
url += "&target="+target
f = urllib.urlopen(url)
s = f.read().decode('UTF-8')
# it seems that the google translate api return html encoded strings
htmlcodes = ('&', '&amp;'),('<', '&lt;'),('>', '&gt;'),('"', '&quot;'),("'", '&#39;')
for c, code in htmlcodes: s = s.replace(code, c)
f.close()
rc = json.loads(s)
if rc.has_key("data"):
translated = rc["data"]["translations"][0]["translatedText"]
if not scriptcontext.sticky.has_key(cache_dict_name):
scriptcontext.sticky[cache_dict_name] = {}
scriptcontext.sticky[cache_dict_name][text] = translated
return translated
# use __name__ test to determine if this script is directly being
# executed or if it is being loaded as a library
if( __name__=="__main__" ):
if rs.ContextIsGrasshopper():
# if this script is running in grasshopper, translate the input
# variable and set the output variables
spanish = translate(english, "en", "es")
german = translate(english, "en", "de")
french = translate(english, "en", "fr")
italian = translate(english, "en", "it")
japanese = translate(english, "en", "ja")
elif rs.ContextIsRhino():
# get text dots and translate their contents
dots = rs.GetObjects("Select dots to translate", rs.filter.textdot)
if dots:
langs = { "English":"en",
"ChineseSimplified":"zh-CN",
"ChineseTraditional":"zh-TW",
"Czech":"cs",
"French":"fr",
"German":"de",
"Italian":"it",
"Japanese":"ja",
"Korean":"ko",
"Polish":"pl",
"Spanish":"es"
}
source_lang = rs.GetString("source", "English", langs.keys())
source_lang = langs[source_lang]
target_lang = rs.GetString("target", "Spanish", langs.keys())
target_lang = langs[target_lang]
for dot in dots:
s = rs.TextDotText(dot)
s = translate(s, source_lang, target_lang)
rs.TextDotText(dot, s)

view raw
googletranslate.py
hosted with ❤ by GitHub

May 2, 2011 / stevebaer

Python component for Grasshopper

Giulio Piacentino and I have been working on an update to the grasshopper/python component and finally have something to show off!!!

http://www.food4rhino.com/project/ghpython – NOTE: Will only work in Rhino 5

This component is a scripting component similar to the VB.NET and C# components that ship with Grasshopper. You get a component where you can define as many input and output variables as you want and then reference these variables in the python script.
ghpython
If you double click on the component, you get a light version of the python script editor which you are used to in the EditPythonScript command (keywords are colorized and functions autocomplete when possible)
ghpython_editor

But there is a cool twist… this component supports the rhinoscriptsyntax functions. The rhinoscriptsyntax functions can be set to generate geometry inside of Grasshopper that does not live in the Rhino document. We are using a concept called “duck typing” to swap the document that the rhinoscriptsyntax functions use from the rhino document to a grasshopper document. This means that the following script

import rhinoscriptsyntax as rs
for x in range(10):
    rs.AddPoint((x,0,0)

will add 10 points to the Rhino document when run from Rhino’s “RunPythonScript” or “EditPythonScript” functions. The same script will add 10 points to a grasshopper document that can be passed on to other grasshopper components when run inside the syntax of grasshopper.

We are writing this as an open source project which is hosted at
https://github.com/mcneel/ghpython
Developers are welcome to download, compile, and tinker with the component source code. If you find bugs or want to add features, let us know and we’ll figure out how to get you more involved in the development process.

You can also log bugs and wishlist items at
https://github.com/mcneel/ghpython/issues

Use your python scripting skills in Rhino 5 for Windows, Rhino 5 for Mac, and in Grasshopper for Rhino 5;)
Thanks,
-Steve

April 11, 2011 / stevebaer

Running scripts from Toolbar buttons

This is only for Windows Rhino right now since you can’t customize toolbar buttons on Mac yet, but once that feature becomes available I’m pretty sure the technique will be the same.

In Rhino, create a new toolbar button and edit it. Consult the Rhino help for for adding and editing toolbar buttons. Now you have three options (that I can think of at the moment) for adding a python script to a toolbar button. All of these involve executing the RunPythonScript command
editbutton

  1. Directly embed the script
    Make sure the first line reads -_RunPythonScript (. Note that there needs to be a space between RunPythonScript and the parentheses.
    Make sure the last line is just a closing parentheses. The lines in between are interpreted as your python script.

    -_RunPythonScript (
    import rhinoscriptsyntax as rs
    point0 = rs.GetPoint("start")
    point1 = rs.GetPoint("end")
    rs.AddLine(point0,point1)
    )
    
  2. Absolute path to the script
    -_RunPythonScript (C:\Users\a-steve\Desktop\CurveLength.py)
  3. Path to script on search path
    If the script is on python’s search path, you can use the following form to let python find the script (use period separate subdirectories.)
    -_RunPythonScript (Samples.CurveLength.py)
March 15, 2011 / stevebaer

Komodo Edit on Mac and RhinoPython

komodoedit

Komodo Edit can be configured to recognize the rhinoscriptsyntax module which provides for editor features like autocomplete and function tooltips.

  • Download and start Komodo Edit – http://www.activestate.com/komodo-edit
  • On the menu, select Komodo->Preferences…
  • Expand “Languages” and select “Python”
  • In the Additional Python Import Directories click on the “add” button and navigate to
    /Users//Application Support/McNeel/Rhinoceros/MacPlugIns/IronPython/settings/lib
  • Click OK, it may take a minute for Komodo Edit to parse all of the files and generate autocomplete information, but once that is done you should be able to type your Mac Rhino python scripts with a little help from Komodo!!

komodosetup

February 25, 2011 / stevebaer

macrhinodebugging

Finally, a plug-in architecture for Mac Rhino!!!
The python plug-in for Mac was our first experiment to see if we could actually get .NET plug-ins to work on Mac Rhino and support this framework on Mac for plug-in development. Well… with the latest build of Mac Rhino, I think we’ve got enough support in the application to start telling people how they can write and debug .NET plug-ins on the Mac.

In order to RUN .NET plug-ins on the Mac, you are going to need:

With the above two items installed, you can test to see if everything is working by installing the python plug-in for Mac Rhino
http://wiki.mcneel.com/rhino/mac/python
If you can run the command “RunPythonScript”, then Mac Rhino is properly configured to use .NET plug-ins!!

Ok, so how does it work???
The python plug-in that you installed has a “.macrhi” file extension. This is really just a zip file that has had the file extension changed so Mac Rhino knows how to deal with it. Try changing the .macrhi file extension back to .zip and looking at the contents in finder.
macrhi_contents
You’ll notice that the zip file contain a single directory named IronPython with a bunch of dll, xml, and rhp files. The RhinoDLR_Python.rhp file is a .NET Rhino plug-in that was built against RhinoCommon. This rhp is loaded by Mac Rhino in a similar fashion as Windows Rhino. When you double click on a macrhi, Rhino unzips the contents of this file to a specific directory on your computer; specifically
~/Library/Application Support/McNeel/Rhinoceros/MacPlugIns/
macplugindir

When Rhino first runs, it walks through each of the directories in MacPlugIns and tries to load every .rhp file. You’ll notice in the previous screenshot that there was another directory called “HelloMonoPlugIn”. This is a plug-in project that I wrote that was entirely built and debugged on Mac Rhino.

Writing your own .NET plug-in for Mac Rhino
Let’s focus on how to write your own “HelloMonoPlugIn” so you can start experimenting with plug-in development on the Mac. First, you are going to need to install some development tools

  • Install XCode – this application comes with you Mac and is on your Mac DVD, but is not installed by default with OSX
  • Install MonoDevelop (version 2.4.2 at the time of this writing) – http://monodevelop.com/

MonoDevelop is the tool that you will use to write and test .NET (mono) plug-ins on Mac Rhino. If you’ve used Visual Studio, you shouldn’t have much difficulty getting used to MonoDevelop.
There are no project wizards yet, so we’re going to need to do this the long way
Fire up MonoDevelop and select “Start a New Solution”
newsolution
Create a new C# Library project named something like “MyMacPlugIn”. Don’t check and of the “Project Features”, you won’t need them. After clicking the OK button, you should have a simple C# library project ready for you to type on.
createproject
We need to add a reference to RhinoCommon.dll to our project. This dll is located inside of the Rhinoceros application bundle that is installed when you installed Mac Rhino. On the menu click Project and then “Edit References…”. Pick the “.NET Assembly” tab and find your way to the Rhinoceros.app inside of the applications directory. I know, the user interface for navigating to the assembly is a little weird in this dialog, but I’ve got faith in you:) You should be able to find RhinoCommon at
/Applications/Rhinoceros.app/Contents/Resources/RhinoCommon.dll
Double click on RhinoCommon.dll and it will be added to your project’s list of references
RhinoCommonReference

October 26, 2010 / stevebaer

ACADIA and MIT wrap up

Just got back from a trip to New York and Boston for the ACADIA conference (http://www.acadia.org/acadia2010/). I want to thank everyone that spent the time to meet with me and talk python. In particular, it was great getting together with
Marc Fornes – http://theverymany.com/
Skylar Tibbits – http://www.sjet.us/

who from what I can tell are doing most of their Rhino oriented scripting in python now. Nice!

July 22, 2010 / stevebaer

StarMaker – An advanced sample

starmaker

Here’s a sample that demonstrates quite a few of the advanced features in Rhino.python. This sample was written and tested on Windows (I doubt it is going to work very well on OSX given that Windows.Forms are being used.) You are going to need to be running at least the July 20th build of Rhino 5 to test this.

StarMaker is composed of two script files; starmaker.py and sliderform.py. Save these files to the same directory.

What does it do? StarMaker just creates a new curve based on points from a closed planar polyline and some input from a form. Run the Polygon command and create a polygon with something like 30 edges. Then run starmaker.py

Some python features demonstrated:

  1. Multiple Script Files – the python script is broken into two files. sliderform.py doesn’t do anything on it’s own and is imported into starmaker.py
  2. Direct use of RhinoCommon – the script uses some of the functionality provided by the rhinoscript python functions, but it really makes heavy use of .NET classes defined in RhinoCommon
  3. Custom Windows Form – This is pretty cool!!! sliderform.py is a custom Wnidows Form user interface. This file was actually created in SharpDevelop (http://www.icsharpcode.net/opensource/sd/). SharpDevelop has a nice user interface editor for creating custom forms which are written as python files. Try opening sliderform.py in SharpDevelop and click on the design button. (See the attached SharpDev image)
  4. Custom Display for Preview Geometry – The red curve, dotted lines, and points in the screenshot are not actual geometry in the rhino document but are drawn using display callbacks (also know as conduits)

Calling RhinoCommon from python hasn’t been covered too much on this site so much of the script may look foreign.

I want to thank Marc Fornes of http://www.theverymany.com for inspiring this sample.
sharpdev

The python scripts can be found here

import System.Drawing
import System.Windows.Forms
from System.Drawing import *
from System.Windows.Forms import *
class SliderForm(Form):
def __init__(self):
self.__value_changed_callback = None
self.InitializeComponent()
self.__disable_events = False
self._trackBar1.Value = 10
self._trackBar2.Value = 10
def InitializeComponent(self):
self._btnOk = System.Windows.Forms.Button()
self._btnCancel = System.Windows.Forms.Button()
self._lbl1 = System.Windows.Forms.Label()
self._trackBar1 = System.Windows.Forms.TrackBar()
self._txtBox1 = System.Windows.Forms.TextBox()
self._trackBar2 = System.Windows.Forms.TrackBar()
self._lbl2 = System.Windows.Forms.Label()
self._txtBox2 = System.Windows.Forms.TextBox()
self._trackBar1.BeginInit()
self._trackBar2.BeginInit()
self.SuspendLayout()
#
# btnOk
#
self._btnOk.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right
self._btnOk.DialogResult = System.Windows.Forms.DialogResult.OK
self._btnOk.Location = System.Drawing.Point(202, 98)
self._btnOk.Name = "btnOk"
self._btnOk.Size = System.Drawing.Size(75, 23)
self._btnOk.TabIndex = 6
self._btnOk.Text = "OK"
self._btnOk.UseVisualStyleBackColor = True
#
# btnCancel
#
self._btnCancel.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right
self._btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel
self._btnCancel.Location = System.Drawing.Point(283, 97)
self._btnCancel.Name = "btnCancel"
self._btnCancel.Size = System.Drawing.Size(75, 23)
self._btnCancel.TabIndex = 7
self._btnCancel.Text = "Cancel"
self._btnCancel.UseVisualStyleBackColor = True
#
# lbl1
#
self._lbl1.Location = System.Drawing.Point(12, 15)
self._lbl1.Name = "lbl1"
self._lbl1.Size = System.Drawing.Size(69, 23)
self._lbl1.TabIndex = 0
self._lbl1.Text = "Ratio (even)"
#
# trackBar1
#
self._trackBar1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right
self._trackBar1.AutoSize = False
self._trackBar1.Location = System.Drawing.Point(87, 12)
self._trackBar1.Maximum = 100
self._trackBar1.Name = "trackBar1"
self._trackBar1.Size = System.Drawing.Size(223, 30)
self._trackBar1.TabIndex = 1
self._trackBar1.TickStyle = System.Windows.Forms.TickStyle.None
self._trackBar1.ValueChanged += self.OnSliderValueChanged
#
# txtBox1
#
self._txtBox1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right
self._txtBox1.Location = System.Drawing.Point(316, 12)
self._txtBox1.Name = "txtBox1"
self._txtBox1.Size = System.Drawing.Size(42, 20)
self._txtBox1.TabIndex = 2
self._txtBox1.TextChanged += self.OnTextBoxChanged
#
# trackBar2
#
self._trackBar2.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right
self._trackBar2.AutoSize = False
self._trackBar2.Location = System.Drawing.Point(87, 54)
self._trackBar2.Maximum = 100
self._trackBar2.Name = "trackBar2"
self._trackBar2.Size = System.Drawing.Size(223, 30)
self._trackBar2.TabIndex = 5
self._trackBar2.TickStyle = System.Windows.Forms.TickStyle.None
self._trackBar2.ValueChanged += self.OnSliderValueChanged
#
# lbl2
#
self._lbl2.Location = System.Drawing.Point(12, 57)
self._lbl2.Name = "lbl2"
self._lbl2.Size = System.Drawing.Size(69, 23)
self._lbl2.TabIndex = 3
self._lbl2.Text = "Ratio (odd)"
#
# m_txtBox2
#
self._txtBox2.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right
self._txtBox2.Location = System.Drawing.Point(316, 54)
self._txtBox2.Name = "txtBox2"
self._txtBox2.Size = System.Drawing.Size(42, 20)
self._txtBox2.TabIndex = 4
self._txtBox2.TextChanged += self.OnTextBoxChanged
#
# SliderForm
#
self.AcceptButton = self._btnOk
self.CancelButton = self._btnCancel
self.ClientSize = System.Drawing.Size(370, 132)
self.Controls.Add(self._lbl1)
self.Controls.Add(self._trackBar1)
self.Controls.Add(self._txtBox1)
self.Controls.Add(self._lbl2)
self.Controls.Add(self._trackBar2)
self.Controls.Add(self._txtBox2)
self.Controls.Add(self._btnOk)
self.Controls.Add(self._btnCancel)
self.MaximizeBox = False
self.MinimizeBox = False
self.MinimumSize = System.Drawing.Size(300, 170)
self.Name = "SliderForm"
self.ShowInTaskbar = False
self.Text = "Slider Form"
self._trackBar1.EndInit()
self._trackBar2.EndInit()
self.ResumeLayout(False)
self.PerformLayout()
def OnSliderValueChanged(self, sender, e):
#__disable_events is used to keep from circular
#events being called
if( self.__disable_events!=True ):
self.__disable_events = True
self._txtBox1.Text = str(self._trackBar1.Value/10.0)
self._txtBox2.Text = str(self._trackBar2.Value/10.0)
self.__disable_events = False
if( self.__value_changed_callback!=None ):
val_even = self._trackBar1.Value/10.0
val_odd = self._trackBar2.Value/10.0
self.__value_changed_callback(val_even, val_odd)
def SetValueChangedCallback(self, callback_object):
self.__value_changed_callback = callback_object
def OnTextBoxChanged(self, sender, e):
#__disable_events is used to keep from circular
#events being called
if( self.__disable_events!=True ):
self.__disable_events = True
try:
self._trackBar1.Value = float(self._txtBox1.Text) * 10.0
self._trackBar2.Value = float(self._txtBox2.Text) * 10.0
except:
self._txtBox1.Text = str(self._trackBar1.Value/10.0)
self._txtBox2.Text = str(self._trackBar2.Value/10.0)
self.__disable_events = False

view raw
sliderform.py
hosted with ❤ by GitHub

import sliderform #SliderForm defined in SharpDevelop
import rhinoscriptsyntax as rs
import Rhino
import System.Drawing
import scriptcontext
import System.Windows.Forms
class SliderFormController():
"""Hook up UI of the SliderForm with some Rhino specific actions"""
def __init__(self, curve_ids, dialog):
# tell the SliderForm about a function to call when the slider values change
dialog.SetValueChangedCallback(self.UpdateTempCurves)
# Set up display conduit events to allow drawing of temporary preview
# geometry while the values are being adjusted by the user
#
# CalculateBoundingBox event is used to adjust the view's clipped bounds.
# Preview geometry may be outside the area of the bounding box of geometry
# in the document and therefore may be clipped if we don't adjust the bbox
Rhino.Display.DisplayPipeline.CalculateBoundingBox += self.OnCalcBoundingBox
# DrawForeground event is used to actually draw some temporary geometry on top
# of everything else. Depth writing/testing are off at this stage so all
# geometry is drawn on top
Rhino.Display.DisplayPipeline.DrawForeground += self.OnDrawForeground
# Get notified of when the form closes so we can remove our conduits
dialog.FormClosed += self.OnSliderFormClosed
# temp_curves and temp_curves_bbox are used for dynamic display
self.__temp_curves = []
self.__temp_curves_bbox = None
# original_curves is a list of (id, centroid, points) for each curve
# provided in the constructor. This is the information we need to
# quickly construct our temp_curves
self.__original_curves = []
for curve_id in curve_ids:
# curve_id is a Guid. Try to get the Curve geometry from this Guid
objref = Rhino.DocObjects.ObjRef(curve_id)
curve_geometry = objref.Curve()
objref.Dispose()
if( curve_geometry!=None and curve_geometry.IsClosed ):
mp = Rhino.Geometry.AreaMassProperties.Compute(curve_geometry)
rc, points = curve_geometry.TryGetPolyline()
if( rc ): self.__original_curves.append( (curve_id, mp.Centroid, points) )
def OnSliderFormClosed(self, sender, e):
# Form has closed. Remove our display conduit
Rhino.Display.DisplayPipeline.DrawForeground -= self.OnDrawForeground
Rhino.Display.DisplayPipeline.CalculateBoundingBox -= self.OnCalcBoundingBox
scriptcontext.doc.Views.Redraw()
def OnDrawForeground(self, sender, e):
# This function is called while updating viewports. Try not to perform
# too many calculations in here to minimize display performance hit.
# Draw each temp_curve in red and a "control" polygon in feedback color
curve_color = System.Drawing.Color.Red
feedback_color = Rhino.ApplicationSettings.AppearanceSettings.FeedbackColor
for points, curve in self.__temp_curves:
e.Display.DrawCurve(curve, curve_color, 1)
e.Display.DrawDottedPolyline( points, feedback_color, True )
e.Display.DrawPoints(points, Rhino.Display.PointStyle.ControlPoint, 2, feedback_color)
def OnCalcBoundingBox(self, sender, e):
# Update the boundingbox to include our temporary curves
if(self.__temp_curves_bbox!=None):
e.IncludeBoundingBox(self.__temp_curves_bbox)
def UpdateTempCurves(self, ratio_offset_even, ratio_offset_odd):
self.__temp_curves = []
self.__temp_curves_bbox=None
for id, centroid, cvs in self.__original_curves:
# "cook" up a temp_curve from the original curve data
temp_points = []
count = len(cvs)
for index, cv in enumerate(cvs):
vector = cv centroid
# use %2 for even/odd test
if( index%2 == 0 or index==(count1)):
vector *= ratio_offset_even
else:
vector *= ratio_offset_odd
point = centroid + vector
temp_points.append(point)
temp_curve = Rhino.Geometry.Curve.CreateControlPointCurve(temp_points, 3)
if( temp_curve!=None ):
self.__temp_curves.append((temp_points,temp_curve))
if( self.__temp_curves_bbox==None ):
self.__temp_curves_bbox = temp_curve.GetBoundingBox(False)
else:
self.__temp_curves_bbox = Rhino.Geometry.BoundingBox.Union( self.__temp_curves_bbox, temp_curve.GetBoundingBox(False))
scriptcontext.doc.Views.Redraw()
def AddCurvesToDocument(self):
# add the temp_curves to the document so they become "real" Rhino geometry
for points, curve in self.__temp_curves:
scriptcontext.doc.Objects.AddCurve(curve)
scriptcontext.doc.Views.Redraw()
def main_function():
"""
main function that runs for this script when it is run as __main__
Returns: True or False indicating success or failure
"""
# get a list of curves – use a custom filter function to
# not allow anything but closed polylines
def onlyclosedpolylines( rhino_object, geometry, component_index ):
if( isinstance(geometry, Rhino.Geometry.Curve) ):
if( geometry.IsPolyline() and geometry.IsClsed ): return True
return False
curves = rs.GetObjects("pick curves (polylines.planar.closed)", rs.filter.curve, custom_filter = onlyclosedpolylines)
if( curves!=None ):
# create the SliderForm and hook a "controller" object to it
dialog = sliderform.SliderForm()
controller = SliderFormController(curves, dialog)
# Show our dialog in a semi-modal way.
if( Rhino.UI.Dialogs.ShowSemiModal( dialog ) == System.Windows.Forms.DialogResult.OK ):
controller.AddCurvesToDocument()
return True
return False
if( __name__ == "__main__" ):
main_function()

view raw
starmaker.py
hosted with ❤ by GitHub