Accuracy and return values from inputbox when default value is a length

It is a pity the inputbox loses precision in that way. I wanted to compare the value returned with another and if it had or hadn’t changed do different things. Thank you for your reply.

thank you for the idea which might work but not as illustrated. I tried it but the input[0] is truncated and so never == the shown value. I will play around with it tomorrow. It is getting a bit late here in Ireland. Thank you again.

`` shown = 12.123456789.to_l.to_s```

locking it as a string seems to work with metric and architectural units here…

john

1 Like

YeeeeH! That works! Thank you for that.

I think this problem must surely have affected other programmers. This will be a useful thread for them to read.

default = 12.123456789
shown = 12.123456789.to_l.to_s
input=UI.inputbox(["Float", "Text"], [shown, "Jim"], "Basic Data")
puts input[0]
puts shown
if input[0] == shown
puts"input[0] #{input[0]} IS equal to shown #{shown}"
else
puts"input[0] #{input[0]} is NOT equal to shown #{shown}"
end

I tried the above with 0 zero as the number to the left of the decimal point and 10000000 left of the decimal point. Both worked fine.

Thank you again

Opps! Having looked at it again I have realized that the comparison works because both numbers have now been truncated. Ah well. It looked so promising.

I could temporarily change the models units accuracy that might help? Best would be if inputbox would leave the original length at its full accuracy.

Thank you for trying nevertheless.

In my plugins I simply check if the strings starts with “~” and if it does I assume the user didn’t modify the value.

1 Like

The accuracy will differ between 32-bit and 64-bit machines, as well.

That would show some really odd values in the inputbox. You’d see things like “0.7187500000000214”, “2.9999999999999982” and “9.999999999999998” all the time instead of values that makes sense to the user.

I think it makes sense to have the inputbox show lengths consistently with the VCB, dimensions and the rest of the SU UI.

1 Like

I like your idea of checking for the tilda except there is the (low) possibility that a user might type it in. I only want the original value returned if accepted by the user. The user could be presented with a rounded version of the number. The problem is that when you convert numbers between unit systems you get long decimals. If inputbox truncates these it is impossible to do comparisons.

I guess that would not matter since you are not passing the value between different machines but from inputbox to the ruby script on the same machine.

I tried changing the precision but realized that running the code on later more accurate machines would fail.

default = 10000000.123456789
shown = 10000000.123456789.to_l
puts"default is #{default}. shown is #{shown}"
options_manager = Sketchup.active_model.options
unitsOptions = options_manager["UnitsOptions"]
lengthPrecision = unitsOptions["LengthPrecision"]
unitsOptions["LengthPrecision"] = 16 #16 decimal places of accuracy

input=UI.inputbox(["Float", "Text"], [shown, "Jim"], "Basic Data")
puts"input[0] is #{input[0]}. shown is #{shown}"
puts"input[0].inspect is #{input[0].inspect}. shown.inspect is #{shown.inspect}"

unitsOptions["LengthPrecision"] = lengthPrecision #puts LengthPrecision back to what it was

output on my machine

default is 10000000.12345679. shown is ~ 254000.00m
input[0] is 254000.0031358024900000m. shown is 254000.0031358024900000m
input[0].inspect is 10000000.123456791. shown.inspect is 10000000.12345679
true

Interestingly .inspect produces different numbers. I wonder why?

Since SketchUp’s dimensions are only accurate to 1/1000", accepting input that’s more accurate is fruitless.
So why not always process the users input rounded to that level of accuracy ?

1 Like

Thank you. The way to do it therefore is to change precision temporarily to 1/1000" and then compare the inputbox return value with the stored variable similarly rounded then reset precision. I can do that.
Thank you every one for you helpful suggestions.

Just one last question. Is SkectchUp’s precision limit held as a Constant on the system? Then I could use that instead of specifying it myself.

SketchUp has an innate accuracy of 1/1000"
Any points closer that that are seen as coincident and with those a tiny edge can’t be created.
However, a tiny edge can exist in SketchUp - but you need to create it [or its container group etc] larger, and then scale it smaller afterwards.
The displayed units as set in Model Info do not directly affect SketchUp inner-precision.
So if you set it to display in mm to 0 dp you see 25mm if its a line exactly 25mm long, but ~25mm if it’s actually 1.000" long [which is 25.4mm, so the approximation ~ is added and the valued rounded - in this case it rounds down since it’s < 0.5mm].
So if the user types in 25.3456789mm it’ll display as ~25mm afterwards and actually be 0.997861374015748032" although SketchUp will round it to 0.9979" ?

I guess the accuracy limit isn’t kept as a Consant like PI?

It isn’t really a question of accuracy. What happens is that after many kinds of operations SketchUp executes a “clean up” operation to assure that the geometry hasn’t degraded due to finite precision computer arithmetic. This operation detects vertices that are closer together than 1/1000" and merges them into one. This does not truncate or modify the position values of the kept vertex, rather, it relinks edges that use one of the vertices to use the other one instead.

The merging threshold value is internal to the SketchUp code and is not accessible via any API. Over the years, extension developers have pried the threshold value out of the SketchUp team, but it has never been documented officially anywhere. Nor has the exact capture shape been disclosed (is it a sphere, a cube, or what?).

The effects that have been discussed in this topic have to do with SketchUp’s handling of length types when they are displayed, which does truncate the displayed value to the number of places given in the user’s units selection, indicating it did so with a leading ‘~’. When such a value makes a round trip via an inputbox, the returned value is truncated to what was displayed.

So, in my mind, the user is really only interested in using / seeing values as set in the Units panel. So I would present them with values in their selected (expected) precision:

model = Sketchup.active_model
units = model.options["UnitsOptions"]

#<Sketchup::OptionsProvider:0x0000000dca6db0>

units.keys

["LengthPrecision", "LengthFormat", "LengthUnit", "LengthSnapEnabled",
"LengthSnapLength", "AnglePrecision", "AngleSnapEnabled",
"SnapAngle", "SuppressUnitsDisplay", "ForceInchDisplay"]

units["LengthPrecision"]

3

:warning: The above are not constants. The user can change them at any time. But coders can use an observer to watch these options collections.

1 Like

If I understand what you are saying then I must proceed as follows.
I postfix values in inputbox with value.to_l so they are presented in the unit and precision format of the user. If I want to compare the return value with a comparison variable I must reformat the comparison variable to the users precision because inputbox will have truncated the return value. inputbox cannot return the original value because it converts it to a string to show in the dialog box and thereby losses the original precision. If you convert the return value to a float then you get a number with invalid numbers in the out of precision end of the number, which looks precise but is wrong.

The code snippet below works on my 64bit HP PC. I would point out that it is necessary to temporarily increase the precision to get an accurate comparison if the units are metres and the precision is 2 or less. I increased it to 4 then reset it when the comparison was done in this snippet.

original = 123456789.125456789
shown = original.to_l

options_manager = Sketchup.active_model.options
unitsOptions = options_manager["UnitsOptions"]
lengthPrecision = unitsOptions["LengthPrecision"]

comparison = original.to_l.round(lengthPrecision)

unitsOptions["LengthPrecision"] = 4

puts"original is #{original}. comparison is #{comparison}. shown is #{shown}"

input=UI.inputbox(["Float", "Text"], [shown, "Jim"], "Basic Data")
returned_value = input[0].to_f.round(lengthPrecision)
puts"returned_value is #{returned_value}"
unitsOptions["LengthPrecision"] = lengthPrecision

if comparison== returned_value
then puts("comparison and returned value are effectively equal")
else puts("comparison and returned value are unequal")
end

Just as a prerequsite, go to the “Ideas” group from the pinned wikilist for Ruby Learning Resources, and read and study Thomas’ tutorial on units and the UI::inputbox() method.


And FYI, (besides Length#to_s(),) there are additional module methods in the API, to create unit formatted strings:

Re your example.

a. It is not nice to change the user’s precision setting without restoring it when done.

b. If I were doing this I think I’d leverage the Length class’ ==() method which does comparisons using SketchUp’s internal tolerance. Because basically that is what really matters. (You’re using Ruby Float class’ comparison method, which brings in the floating point madness into the picture.)

Thank you for your helpful links and advice. I found the articles by Thomas interesting.
a/ I reset the precision with the line quoted below from my example.

Or is there something not right about resetting it in this way?

b/ I tried that but the comparison doesn’t work unless I convert it to a string and back into a length. Below is the code I tried first, which doesn’t work.

original = 123456789.125456789
shown = original.to_l
comparison = original.to_l
puts"original is #{original}. shown is #{shown}. comparison is #{comparison}."
input=UI.inputbox(["Float", "Text"], [shown, "Jim"], "Basic Data")
returned_value = input[0].to_l
puts"returned_value is #{returned_value}"
if comparison == returned_value
then puts("comparison and returned value are effectively equal")
else puts("comparison and returned value are unequal")
end

Below is the code where the variable comparison is first converted to length, then to string and finally back to length. This comparison seems to work, even if it is a bit of a palaver!

original = 123456789.125456789
shown = original.to_l
comparison = original.to_l.to_s.to_l
puts"original is #{original}. shown is #{shown}. comparison is #{comparison}."
input=UI.inputbox(["Float", "Text"], [shown, "Jim"], "Basic Data")
returned_value = input[0].to_l
puts"returned_value is #{returned_value}"
if comparison == returned_value
then puts("comparison and returned value are effectively equal")
else puts("comparison and returned value are unequal")
end

Thank you again everyone for your help.

1 Like