Matrix.cpp (5797B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "Matrix.h" 8 #include "Quaternion.h" 9 #include "Tools.h" 10 #include <ostream> 11 #include <math.h> 12 #include <float.h> // for FLT_EPSILON 13 14 #include "mozilla/FloatingPoint.h" // for UnspecifiedNaN 15 16 namespace mozilla { 17 namespace gfx { 18 19 /* Force small values to zero. We do this to avoid having sin(360deg) 20 * evaluate to a tiny but nonzero value. 21 */ 22 double FlushToZero(double aVal) { 23 // XXX Is double precision really necessary here 24 if (-FLT_EPSILON < aVal && aVal < FLT_EPSILON) { 25 return 0.0f; 26 } else { 27 return aVal; 28 } 29 } 30 31 /* Computes tan(aTheta). For values of aTheta such that tan(aTheta) is 32 * undefined or very large, SafeTangent returns a manageably large value 33 * of the correct sign. 34 */ 35 double SafeTangent(double aTheta) { 36 // XXX Is double precision really necessary here 37 const double kEpsilon = 0.0001; 38 39 /* tan(theta) = sin(theta)/cos(theta); problems arise when 40 * cos(theta) is too close to zero. Limit cos(theta) to the 41 * range [-1, -epsilon] U [epsilon, 1]. 42 */ 43 44 double sinTheta = sin(aTheta); 45 double cosTheta = cos(aTheta); 46 47 if (cosTheta >= 0 && cosTheta < kEpsilon) { 48 cosTheta = kEpsilon; 49 } else if (cosTheta < 0 && cosTheta >= -kEpsilon) { 50 cosTheta = -kEpsilon; 51 } 52 return FlushToZero(sinTheta / cosTheta); 53 } 54 55 template <> 56 Matrix Matrix::Rotation(Float aAngle) { 57 Matrix newMatrix; 58 59 Float s = sinf(aAngle); 60 Float c = cosf(aAngle); 61 62 newMatrix._11 = c; 63 newMatrix._12 = s; 64 newMatrix._21 = -s; 65 newMatrix._22 = c; 66 67 return newMatrix; 68 } 69 70 template <> 71 MatrixDouble MatrixDouble::Rotation(Double aAngle) { 72 MatrixDouble newMatrix; 73 74 Double s = sin(aAngle); 75 Double c = cos(aAngle); 76 77 newMatrix._11 = c; 78 newMatrix._12 = s; 79 newMatrix._21 = -s; 80 newMatrix._22 = c; 81 82 return newMatrix; 83 } 84 85 template <> 86 Matrix4x4 MatrixDouble::operator*(const Matrix4x4& aMatrix) const { 87 Matrix4x4 resultMatrix; 88 89 resultMatrix._11 = this->_11 * aMatrix._11 + this->_12 * aMatrix._21; 90 resultMatrix._12 = this->_11 * aMatrix._12 + this->_12 * aMatrix._22; 91 resultMatrix._13 = this->_11 * aMatrix._13 + this->_12 * aMatrix._23; 92 resultMatrix._14 = this->_11 * aMatrix._14 + this->_12 * aMatrix._24; 93 94 resultMatrix._21 = this->_21 * aMatrix._11 + this->_22 * aMatrix._21; 95 resultMatrix._22 = this->_21 * aMatrix._12 + this->_22 * aMatrix._22; 96 resultMatrix._23 = this->_21 * aMatrix._13 + this->_22 * aMatrix._23; 97 resultMatrix._24 = this->_21 * aMatrix._14 + this->_22 * aMatrix._24; 98 99 resultMatrix._31 = aMatrix._31; 100 resultMatrix._32 = aMatrix._32; 101 resultMatrix._33 = aMatrix._33; 102 resultMatrix._34 = aMatrix._34; 103 104 resultMatrix._41 = 105 this->_31 * aMatrix._11 + this->_32 * aMatrix._21 + aMatrix._41; 106 resultMatrix._42 = 107 this->_31 * aMatrix._12 + this->_32 * aMatrix._22 + aMatrix._42; 108 resultMatrix._43 = 109 this->_31 * aMatrix._13 + this->_32 * aMatrix._23 + aMatrix._43; 110 resultMatrix._44 = 111 this->_31 * aMatrix._14 + this->_32 * aMatrix._24 + aMatrix._44; 112 113 return resultMatrix; 114 } 115 116 // Intersect the polygon given by aPoints with the half space induced by 117 // aPlaneNormal and return the resulting polygon. The returned points are 118 // stored in aDestBuffer, and its meaningful subspan is returned. 119 template <typename F> 120 Span<Point4DTyped<UnknownUnits, F>> IntersectPolygon( 121 Span<Point4DTyped<UnknownUnits, F>> aPoints, 122 const Point4DTyped<UnknownUnits, F>& aPlaneNormal, 123 Span<Point4DTyped<UnknownUnits, F>> aDestBuffer) { 124 if (aPoints.Length() < 1 || aDestBuffer.Length() < 1) { 125 return {}; 126 } 127 128 size_t nextIndex = 0; // aDestBuffer[nextIndex] is the next emitted point. 129 130 // Iterate over the polygon edges. In each iteration the current edge 131 // is the edge from *prevPoint to point. If the two end points lie on 132 // different sides of the plane, we have an intersection. Otherwise, 133 // the edge is either completely "inside" the half-space created by 134 // the clipping plane, and we add curPoint, or it is completely 135 // "outside", and we discard curPoint. This loop can create duplicated 136 // points in the polygon. 137 const auto* prevPoint = &aPoints[aPoints.Length() - 1]; 138 F prevDot = aPlaneNormal.DotProduct(*prevPoint); 139 for (const auto& curPoint : aPoints) { 140 F curDot = aPlaneNormal.DotProduct(curPoint); 141 142 if ((curDot >= 0.0) != (prevDot >= 0.0)) { 143 // An intersection with the clipping plane has been detected. 144 // Interpolate to find the intersecting curPoint and emit it. 145 F t = -prevDot / (curDot - prevDot); 146 aDestBuffer[nextIndex++] = curPoint * t + *prevPoint * (1.0 - t); 147 if (nextIndex >= aDestBuffer.Length()) { 148 break; 149 } 150 } 151 152 if (curDot >= 0.0) { 153 // Emit any source points that are on the positive side of the 154 // clipping plane. 155 aDestBuffer[nextIndex++] = curPoint; 156 if (nextIndex >= aDestBuffer.Length()) { 157 break; 158 } 159 } 160 161 prevPoint = &curPoint; 162 prevDot = curDot; 163 } 164 165 return aDestBuffer.To(nextIndex); 166 } 167 168 template Span<Point4DTyped<UnknownUnits, Float>> IntersectPolygon( 169 Span<Point4DTyped<UnknownUnits, Float>> aPoints, 170 const Point4DTyped<UnknownUnits, Float>& aPlaneNormal, 171 Span<Point4DTyped<UnknownUnits, Float>> aDestBuffer); 172 template Span<Point4DTyped<UnknownUnits, Double>> IntersectPolygon( 173 Span<Point4DTyped<UnknownUnits, Double>> aPoints, 174 const Point4DTyped<UnknownUnits, Double>& aPlaneNormal, 175 Span<Point4DTyped<UnknownUnits, Double>> aDestBuffer); 176 177 } // namespace gfx 178 } // namespace mozilla