There are extensions to the base classes in SketchUp’s Ruby API which often new SketchUp plugin developers overlook. If you aren’t aware of them you might find yourself reinventing many features of the SketchUp API which already exists. There is very little you need to do to deal with units, SketchUp does most of the work.
Internally SketchUp uses inches as system unit. All 3D coordinates in the model data are in inches – no exceptions! This internal units are converted to model units only when presented in the UI. Similarly, all user input is immediately converted to internal units. Your plugin should do this to!
Internal Units to Model Units
To convert an internal unit, to a string in model unit, ensure that your length value is of the Length
class. Length.to_s
– which is the same as Sketchup.format_length( value )
– formats the internal unit into a user friendly string using the model settings and user locale.
Float
and Length
Length
is a sub-class of Float
. Geom::Point3d
and Geom::Vector3d
will return Length
objects for it’s coordinate components.
Note that using Length
for arithmetic operations will return Float
objects. So make sure you know whether you have a Float
or Length
object when you eventually want to present that value to the UI. Float.to_l
will give you a Length
object when you need to.
Model Units to Internal Units
The String
class takes care of converting model units to internal units in the form of String.to_l
. If you have a string "50".to_l
and the model units is millimetres it will assume the numeric value in the string is millimetres. But at the same time the string can contain unit indicators to override this: "50m".to_l
will be treated as 50 metres regardless of model unit settings. This is useful for processing VCB input for instance as the user can then input lengths in any units supported by SketchUp – just like the native tools and you don’t have to do anything more than .to_l
.
UI.inputbox
The input box in SketchUp will actually try and do the conversion between model units and user input strings for you if you set it up correctly. If you give it Length
objects as default values it will automatically convert the values into model unit strings in the UI of the dialogue and back into Length for the return values.
1 2 3 4 5 6 7 8 9 10 | prompts = ['Name', 'Width', 'Height'] defaults = ['My Own Square', 5.m, 2.m] input = UI.inputbox( prompts, defaults, 'Create Square' ) # User enters Width: 300cm, Height 4 p input # => ["My Own Square", 118.110236220472, 157.48031496063] p input.map { |n| n.class } # => [String, Length, Length] p input.map { |n| n.to_s } # => ["My Own Square", "3000mm", "4000mm"] |
prompts = ['Name', 'Width', 'Height'] defaults = ['My Own Square', 5.m, 2.m] input = UI.inputbox( prompts, defaults, 'Create Square' ) # User enters Width: 300cm, Height 4 p input # => ["My Own Square", 118.110236220472, 157.48031496063] p input.map { |n| n.class } # => [String, Length, Length] p input.map { |n| n.to_s } # => ["My Own Square", "3000mm", "4000mm"]
As you can see from this example, SketchUp does all the work – you do not need to convert to and from string yourself.
Note that this doesn’t work so well for floats, as Ruby assumes floats are using period for decimal separator even if the user locale uses comma. If the user uses a comma for decimal separator for Float
values you get an ArgumentError
when SketchUp tries to convert the String
back to Float
. This error is unique to UI.inputbox
– when you otherwise convert for instance '0,8'.to_f
you will get 0.0
without any errors.
Storing Unit Data
Because users have different decimal separators depending on their locale, never store unit data as formatted strings! Don’t write Length
values out as length.to_s
– instead convert it to a Float
. That way you can be sure that you can read the data back regardless of the locale of the target system because Float.to_s
always use period as decimal separator – and String.to_f
always expect a period. To load a unit from a float stored as string correctly you use: string.to_f.to_l
.
A string like "10,5m
” can only be parsed with .to_l
correctly on systems with comma as decimal separator – similarly "10.5m"
can only be parsed with .to_l
correctly on systems with period as decimal separator. Mixing up this and you end up with ArgumentError
.
Angles
Internally SketchUp uses radians instead of degrees. The Numeric
class is extended to provide easy conversions so you can type stuff like 75.degrees
which will return 75 degrees in radians – similarly you can type 0.25.radians
to return a radian value to degrees. These methods also exists as SketchUp.format_degrees
and Sketchup.format_angle
.
Areas
You also have Sketchup.format_area
which will take a value representing an area inch-squared and format it into model units. But the problem arise when you want to convert the other way around. Currently, SketchUp 8 M3, there is no methods for converting area strings in model units to internal units. You have to craft your own. This is not so easy because some user locales uses comma as decimal separator – and there is no method that returns this in SketchUp. But you can hack it.
SketchUp Units and Locale Helper Library
I’ve collected most of my units and locale methods into an example library on GitHub. Feel free to use it for whatever you need and please push back improvements.
3 Comments
Leave a commentThank you for explanation.
How deals with model units in html/javascript webdialog ui ?
Sorry for my bad english.
Before sending the units over to the WebDialog I convert the
Length
into aString
so I get the length formatted in the current model format. And if I need to pull values back I pull them back as strings and convert them back toLength
by usingString.to_l
.So basically I thread it just as I would in SketchUp – output to string into the UI, and when I need user input I take a user string and convert into Length.
If you have a spesific example I could give a spesific answer.
[…] Let SketchUp handle units […]