Skip to content

Commit 9ecd959

Browse files
Simon RitSimonRit
authored andcommitted
ENH: Avoid division by 0 in ray quadric intersection
Ray quadric intersection comes down to solving a quadratic equation. If it simplifies to a linear equation, check what happens if the line is parallel to the quadric. Based on #819 (comment)
1 parent 9ac396c commit 9ecd959

File tree

1 file changed

+86
-45
lines changed

1 file changed

+86
-45
lines changed

src/rtkQuadricShape.cxx

Lines changed: 86 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,52 @@ QuadricShape ::IsIntersectedByRay(const PointType & rayOrigin,
6363
const ScalarType zero = itk::NumericTraits<ScalarType>::ZeroValue();
6464
if (Aq == zero)
6565
{
66-
// Only one intersection with, e.g., a plane: check which half line should be kept
67-
nearDist = -Cq / Bq;
68-
// The following condition tests on which side of the quadric is the ray origin.
69-
// It is equivalent to the following code.
70-
// if ((IsInsideQuadric(rayOrigin) && nearDist < 0.) ||
71-
// (!IsInsideQuadric(rayOrigin) && nearDist > 0.))
72-
if (Cq * nearDist > 0.)
73-
farDist = itk::NumericTraits<ScalarType>::max();
66+
if (Bq == zero)
67+
{
68+
if (Cq == zero)
69+
{
70+
// Whole ray contained in quadric
71+
nearDist = -itk::NumericTraits<ScalarType>::max();
72+
farDist = itk::NumericTraits<ScalarType>::max();
73+
}
74+
else
75+
return false;
76+
}
7477
else
7578
{
76-
farDist = nearDist;
77-
nearDist = -itk::NumericTraits<ScalarType>::max();
79+
if (Cq == zero)
80+
{
81+
// rayOrigin is on the quadric, we need to find which half line is in
82+
// the quadric For t=1, we have Aq+Bq+Cq, in this case Bq. As Aq and Cq
83+
// are negative, we test the sign of Bq.
84+
if (Bq > 0)
85+
{
86+
// rayOrigin+rayDirection is not in the quadric
87+
farDist = 0.;
88+
nearDist = -itk::NumericTraits<ScalarType>::max();
89+
}
90+
else
91+
{
92+
nearDist = 0.;
93+
farDist = itk::NumericTraits<ScalarType>::max();
94+
}
95+
}
96+
else
97+
{
98+
// Only one intersection with, e.g., a plane: check which half line should be kept
99+
nearDist = -Cq / Bq;
100+
// The following condition tests on which side of the quadric is the ray origin.
101+
// It is equivalent to the following code.
102+
// if ((IsInsideQuadric(rayOrigin) && nearDist < 0.) ||
103+
// (!IsInsideQuadric(rayOrigin) && nearDist > 0.))
104+
if (Cq * nearDist > 0.)
105+
farDist = itk::NumericTraits<ScalarType>::max();
106+
else
107+
{
108+
farDist = nearDist;
109+
nearDist = -itk::NumericTraits<ScalarType>::max();
110+
}
111+
}
78112
}
79113
}
80114
else
@@ -83,7 +117,12 @@ QuadricShape ::IsIntersectedByRay(const PointType & rayOrigin,
83117
// The epsilon value allows detection of very close intersections, i.e., a
84118
// ray tangent to the quadric
85119
static constexpr ScalarType eps = 1e5 * itk::NumericTraits<ScalarType>::epsilon();
86-
if (discriminant <= eps)
120+
if (itk::Math::abs(discriminant) <= eps)
121+
{
122+
nearDist = -Bq / (2 * Aq);
123+
farDist = nearDist;
124+
}
125+
else if (discriminant < zero)
87126
{
88127
// No intersection but one might be dealing with an infinite line
89128
// in the quadric, e.g. a line parallel to and in a cylinder.
@@ -92,52 +131,54 @@ QuadricShape ::IsIntersectedByRay(const PointType & rayOrigin,
92131
{
93132
nearDist = -itk::NumericTraits<ScalarType>::max();
94133
farDist = itk::NumericTraits<ScalarType>::max();
95-
return ApplyClipPlanes(rayOrigin, rayDirection, nearDist, farDist);
96134
}
97135
else
98136
return false;
99137
}
100-
nearDist = (-Bq - sqrt(discriminant)) / (2 * Aq);
101-
farDist = (-Bq + sqrt(discriminant)) / (2 * Aq);
102-
if (nearDist > farDist)
103-
std::swap(nearDist, farDist);
104-
105-
// Check if the segment between the two intersections is in the quadric.
106-
// Instead of testing if the central point is oustside the quadric with
107-
// !IsInsideQuadric(rayOrigin + 0.5 * (farDist + nearDist) * rayDirection),
108-
// which was prone to numerical errors, we test if the ray origin is between
109-
// intersections (farDist * nearDist < 0) and strictly in the quadric (Cq < 0),
110-
// or before / after the two intersections (farDist * nearDist > 0) and outside
111-
// the quadric (Cq>0).
112-
if (Cq * farDist * nearDist < zero)
138+
else
113139
{
114-
// If not, one is dealing with a half line, searching for the good one (out of two)
115-
double tmpNearDist = -itk::NumericTraits<ScalarType>::max();
116-
double tmpFarDist = itk::NumericTraits<ScalarType>::max();
117-
if (!ApplyClipPlanes(rayOrigin, rayDirection, tmpNearDist, tmpFarDist))
118-
return false;
119-
if (tmpFarDist > farDist)
140+
nearDist = (-Bq - sqrt(discriminant)) / (2 * Aq);
141+
farDist = (-Bq + sqrt(discriminant)) / (2 * Aq);
142+
if (nearDist > farDist)
143+
std::swap(nearDist, farDist);
144+
145+
// Check if the segment between the two intersections is in the quadric.
146+
// Instead of testing if the central point is oustside the quadric with
147+
// !IsInsideQuadric(rayOrigin + 0.5 * (farDist + nearDist) * rayDirection),
148+
// which was prone to numerical errors, we test if the ray origin is between
149+
// intersections (farDist * nearDist < 0) and strictly in the quadric (Cq < 0),
150+
// or before / after the two intersections (farDist * nearDist > 0) and outside
151+
// the quadric (Cq>0).
152+
if (Cq * farDist * nearDist < zero)
120153
{
121-
if (tmpNearDist < nearDist)
154+
// If not, one is dealing with a half line, searching for the good one (out of two)
155+
double tmpNearDist = -itk::NumericTraits<ScalarType>::max();
156+
double tmpFarDist = itk::NumericTraits<ScalarType>::max();
157+
if (!ApplyClipPlanes(rayOrigin, rayDirection, tmpNearDist, tmpFarDist))
158+
return false;
159+
if (tmpFarDist > farDist)
122160
{
123-
itkGenericExceptionMacro(<< "Intersection of the quadric with the line "
124-
<< "gives two half lines, add clip planes to select which one");
161+
if (tmpNearDist < nearDist)
162+
{
163+
itkGenericExceptionMacro(<< "Intersection of the quadric with the line "
164+
<< "gives two half lines, add clip planes to select which one");
165+
}
166+
else
167+
{
168+
nearDist = std::max(farDist, tmpNearDist);
169+
farDist = tmpFarDist;
170+
return true;
171+
}
125172
}
126-
else
173+
else if (tmpNearDist < nearDist)
127174
{
128-
nearDist = std::max(farDist, tmpNearDist);
129-
farDist = tmpFarDist;
175+
farDist = std::min(nearDist, tmpFarDist);
176+
nearDist = tmpNearDist;
130177
return true;
131178
}
179+
else
180+
return false;
132181
}
133-
else if (tmpNearDist < nearDist)
134-
{
135-
farDist = std::min(nearDist, tmpFarDist);
136-
nearDist = tmpNearDist;
137-
return true;
138-
}
139-
else
140-
return false;
141182
}
142183
}
143184

0 commit comments

Comments
 (0)