top of page
Trimble_Logo.svg.png
SketchUp-Horizontal-RGB-120pxl_0.png

Handling The ~ Mark

Chapters

Screenshot 2025-04-08 063839.png

What it means for the user

The ~ mark, or tilde, is known to regular SketchUp users. It's sometimes shown in the measurement box (formerly called VCB) when you trace a distance in the 3D view that is close to, but not identical, to a nice whole number.

 

It's easy for long-time SketchUp modelers to be disgruntled at this mark - it does show up when we have some minor imprecision in our models - but I think we shouldn't be too hard on this little typographical curiosity. Don't shoot the messenger!

 

Writing a distance as "~ 1.0 m" is SketchUp's alternative to writing something like "1.0002324 m". It allows us to see directly that the measurement is slightly off, without having to mentally parse all those pesky little decimals. It also allows SketchUp to say "1.0 m" and not "1.0000 m" with a  bunch of trailing zeros. This is a great example of SketchUp's human centric design. Instead of printing the full internal value as a lesser software would, SketchUp expresses it similar to how a person would. And if you want to see more decimals, you can always increase the display precision in Model Info > Units.

What it means for developers

When displaying a value to the user, we don't need to think about "~". As long as we use SketchUp's Length class, it's handled automatically.

# Assume model displays in mm with 0 decimals
options = Sketchup.active_model.options
options["UnitsOptions"]["LengthFormat"] = Length::Decimal
options["UnitsOptions"]["LengthUnit"] = Length::Millimeter
options["UnitsOptions"]["LengthPrecision"] = 0

 

# Always use the Length class to express distances
value = 1.00001.m


value.to_s
# => "~ 1000 mm"

The problem arises in some situations when we parse user input. A user typically wouldn't type this symbol, but if they accept a default value containing it, we have to handle it. This typically happens when the default value was defined in a different unit than what the user is currently using.

# Assume model displays in mm with 0 decimals
options = Sketchup.active_model.options
options["UnitsOptions"]["LengthFormat"] = Length::Decimal
options["UnitsOptions"]["LengthUnit"] = Length::Millimeter
options["UnitsOptions"]["LengthPrecision"] = 0

 

# Always use the Length class to express distances
value = 1.00001.m


value.to_s
# => "~ 1000 mm"

# Assume model displays in mm with 0 decimals
options = Sketchup.active_model.options
options["UnitsOptions"]["LengthFormat"] = Length::Decimal
options["UnitsOptions"]["LengthUnit"] = Length::Millimeter
options["UnitsOptions"]["LengthPrecision"] = 0

# But default value happens to be in inches
default = 4.inch

# Value displays as "~102 mm"
# Press OK without changing the value
input = UI.inputbox(["Timber Size"], [default], "Timber Plugin")
value = input[0]


value.inch
# => 4.015748031496063

# Not 4 inches as expected :(

What Sketchup did here was to first convert the number to the model's display units, with rounding, and then parse it back to a length object. SketchUp ignores the "~" sign and gives us the same value as if the user had entered "102 mm". This value doesn't perfectly map to our original value!

 

What we can do about it is check if the value starts with a ~ mark, and if so fall back on the default value.

# Assume model displays in mm with 0 decimals
options = Sketchup.active_model.options
options["UnitsOptions"]["LengthFormat"] = Length::Decimal
options["UnitsOptions"]["LengthUnit"] = Length::Millimeter
options["UnitsOptions"]["LengthPrecision"] = 0

 

# Always use the Length class to express distances
value = 1.00001.m


value.to_s
# => "~ 1000 mm"

# Assume model displays in mm with 0 decimals
options = Sketchup.active_model.options
options["UnitsOptions"]["LengthFormat"] = Length::Decimal
options["UnitsOptions"]["LengthUnit"] = Length::Millimeter
options["UnitsOptions"]["LengthPrecision"] = 0

# But default value happens to be in inches
default = 4.inch

# Value displays as "~102 mm"
# Convert Length to String so we can check the individual characters
# Press OK without changing the value
input = UI.inputbox(["Timber Size"], [default.to_s], "Timber Plugin")
value = input[0]
if value.start_with?("~")
  value = default
end
# Explicitly convert back to Length
value = value.to_l
# 4 inches, as expected. Woohoo! :D
value.inch
# => 4.0

That works in most cases, but assumes the user doesn't change any digits after the tilde mark. A more reliable way would be to also compare the values after they've been formatted as strings.

# Assume model displays in mm with 0 decimals
options = Sketchup.active_model.options
options["UnitsOptions"]["LengthFormat"] = Length::Decimal
options["UnitsOptions"]["LengthUnit"] = Length::Millimeter
options["UnitsOptions"]["LengthPrecision"] = 0

# But default value happens to be in inches
default = 4.inch

# Value displays as "~102 mm"
# Convert Length to String so we can check the individual characters
# Play around with the input. Try different numbers with or without ~ and
input = UI.inputbox(["Timber Size"], [default.to_s], "Timber Plugin")
value = input[0]
if value.start_with?("~") && value.to_s == default.to_s
  value = default
end
# Explicitly convert back to Length
value = value.to_l

value.inch
# => 4.0

This gives us a robust way to handle user input in all edge cases.


Smarter Default Values
 
But why would the default value be defined in different units from the model unit to begin with? While it's easy to just define a single default value for all users, we can actually define separate defaults depending on the system of measurements used.

def metric?
  unit_options = Sketchup.active_model.options["UnitsOptions"]
  return false unless unit_options["LengthFormat"] == Length::Decimal
 
  [Length::Millimeter, Length::Centimeter,
  Length::Meter].include?(unit_options["LengthUnit"])
end

default = metric? ? 100.mm : 4.inch
puts default

This gives a nice round number for both metric and imperial users. While the values don't correspond to the exact same distance, it's often more relevant to have a reasonable number without seemingly random decimals.
 
So, if we provide reasonable defaults can we then skip the ~ mark handling? Not necessarily. The default value shown in the inputbox doesn't have to be a value defined by the extension, but can be input from a previous user. In these cases it's best to check for a ~ mark.
 
Another scenario is when the precision set up by the user for the current model rounds a small value to zero. For instance, an urban planner or landscape architect may work in meters with one decimal, but use an extension to draw fences with the metal wire diameter defaulting to 3 mm. Changing the unit system of the default won't fix this, but handling the ~ mark will.


Reflection
While not necessary for an extension to function, this is one of those details that allows you to feel extra proud of your work. It fixes a bug in certain edge cases and makes the extension work a little better. You can use it to show off to your client what a good extension developer you are, to make users happier and reduce the support load.

bottom of page