We talked about using the Double.Epsilon and our own epsilon values in Revit addins before and mentioned the FilterDoubleRule a bit since it needs an epsilon to be specified always.
In this post, let us look at a sample case and verify the assertion we made before, the Double.Epsilon should not be used for filter rules at all. Instead, our own epsilon values like 10e-8 or 10e-10 are good to use, which are not too big or too small, just reasonable enough.
Ok, the task is that we’d like to find all walls which lengths are in whole number feet.
If the Double.Epsilon is used for the FilterDoubleRule instances, the code looks like this:
public static ICollection<ElementId> FindWallsWithLengthInWholeNumbersLessThan100UsingDoubleEpsilon(RvtDocument doc)
{
ParameterValueProvider provider = new ParameterValueProvider(new ElementId((int)BuiltInParameter.CURVE_ELEM_LENGTH));
ElementParameterFilter filter = new ElementParameterFilter(new FilterDoubleRule(provider, new FilterNumericEquals(), 0, Double.Epsilon));
LogicalOrFilter orFilter = new LogicalOrFilter(filter, filter);
for (int i = 1; i < 100; i++)
{
FilterDoubleRule rule = new FilterDoubleRule(provider, new FilterNumericEquals(), i, Double.Epsilon);
filter = new ElementParameterFilter(rule);
orFilter = new LogicalOrFilter(orFilter, filter);
}
FilteredElementCollector wallCollector = new FilteredElementCollector(doc)
.OfClass(typeof(Autodesk.Revit.DB.Wall))
.WherePasses(orFilter);
return wallCollector.ToElementIds();
}
We also demonstrated in the code how to use Revit API filter relevant classes (like ParameterValueProvider, ElementParamererFilter, FilterDoubleRule, LogicalOrFilter, and FilteredElementCollector) in a loop to fufill some complex taskes.
Otherwise, if we want to use own own epsilon like 10e-10 for the FilterDoubleRule, the code will be:
public static ICollection<ElementId> FindWallsWithLengthInWholeNumbersLessThan100UsingOurOwnEpsilon(RvtDocument doc)
{
ParameterValueProvider provider = new ParameterValueProvider(new ElementId((int)BuiltInParameter.CURVE_ELEM_LENGTH));
ElementParameterFilter filter = new ElementParameterFilter(new FilterDoubleRule(provider, new FilterNumericEquals(), 0, 10e-10));
LogicalOrFilter orFilter = new LogicalOrFilter(filter, filter);
for (int i = 1; i < 100; i++)
{
FilterDoubleRule rule = new FilterDoubleRule(provider, new FilterNumericEquals(), i, 10e-10);
filter = new ElementParameterFilter(rule);
orFilter = new LogicalOrFilter(orFilter, filter);
}
FilteredElementCollector wallCollector = new FilteredElementCollector(doc)
.OfClass(typeof(Autodesk.Revit.DB.Wall))
.WherePasses(orFilter);
return wallCollector.ToElementIds();
}
Some people may wonder what the differences really are here.
Using the following code to test the two methods,
ICollection<ElementId> wallsFound1 = FindWallsWithLengthInWholeNumbersLessThan100UsingDoubleEpsilon(CachedDoc);
ICollection<ElementId> wallsFound2 = FindWallsWithLengthInWholeNumbersLessThan100UsingOurOwnEpsilon(CachedDoc);
MessageBox.Show(string.Format("Found {0} walls using the Double.Epsilon.\nFound {1} walls using our own epsilon.", wallsFound1.Count, wallsFound2.Count), "ElementParameterFilter and Epsilon");
things will become very clear.
As can be seen, the Double.Epsilon works sometimes but does not most of times. It misses out 15 walls in the not so big test model. Our own epsilon 10e-10 works perfectly and finds out all walls as expected.
We also learned something from the case: something works sometimes does not mean it always.
Is there anything wrong with the Double.Epsilon?
Not at all. It is clearly stated in the MSDN documentation that the Double.Epsilon is designed for some other purpose and should not be used to compare double values as a general measure of precision.
“The value of this constant is 4.94065645841247e-324.”
“Represents the smallest positive Double value that is greater than zero. This field is constant.”
Obviously too small is it!
“However, the Epsilon property is not a general measure of precision of the Double type; it applies only to Double instances that have a value of zero. “
As far as zero is concerned, the == operator may also just work.
“The value of the Epsilon property is not equivalent to machine epsilon, which represents the upper bound of the relative error due to rounding in floating-point arithmetic.”
So this epsilon is not that epsilon!
Now it is clear that the Double.Epsilon should not be used for the double value comparison purpose regardless of in our own methods or in Revit API ones like the FilterDoubleRule.
It is recommended again to use the 10e-10 or a similar value as the epsilon for double value comparisons as far as Revit API is concerned as Revit products are designed for architecture, structure, or mechanical worlds.
If there could be a case that somebody uses the Revit to draw the atom structure or something like that, it would really make a difference. In that case, however, a different product with a different internal unit like nanometer or smaller might be a better choice.
RevitAddinWizard follows the good practice in its various coders.
Recent Comments