summaryrefslogtreecommitdiffstats
path: root/gfx/2d/Matrix.cpp
blob: cb8830c1688f020883a730666783f9e3d8629556 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "Matrix.h"
#include "Quaternion.h"
#include "Tools.h"
#include <algorithm>
#include <ostream>
#include <math.h>
#include <float.h>  // for FLT_EPSILON

#include "mozilla/FloatingPoint.h"  // for UnspecifiedNaN

namespace mozilla {
namespace gfx {

/* Force small values to zero.  We do this to avoid having sin(360deg)
 * evaluate to a tiny but nonzero value.
 */
double FlushToZero(double aVal) {
  // XXX Is double precision really necessary here
  if (-FLT_EPSILON < aVal && aVal < FLT_EPSILON) {
    return 0.0f;
  } else {
    return aVal;
  }
}

/* Computes tan(aTheta).  For values of aTheta such that tan(aTheta) is
 * undefined or very large, SafeTangent returns a manageably large value
 * of the correct sign.
 */
double SafeTangent(double aTheta) {
  // XXX Is double precision really necessary here
  const double kEpsilon = 0.0001;

  /* tan(theta) = sin(theta)/cos(theta); problems arise when
   * cos(theta) is too close to zero.  Limit cos(theta) to the
   * range [-1, -epsilon] U [epsilon, 1].
   */

  double sinTheta = sin(aTheta);
  double cosTheta = cos(aTheta);

  if (cosTheta >= 0 && cosTheta < kEpsilon) {
    cosTheta = kEpsilon;
  } else if (cosTheta < 0 && cosTheta >= -kEpsilon) {
    cosTheta = -kEpsilon;
  }
  return FlushToZero(sinTheta / cosTheta);
}

template <>
Matrix Matrix::Rotation(Float aAngle) {
  Matrix newMatrix;

  Float s = sinf(aAngle);
  Float c = cosf(aAngle);

  newMatrix._11 = c;
  newMatrix._12 = s;
  newMatrix._21 = -s;
  newMatrix._22 = c;

  return newMatrix;
}

template <>
MatrixDouble MatrixDouble::Rotation(Double aAngle) {
  MatrixDouble newMatrix;

  Double s = sin(aAngle);
  Double c = cos(aAngle);

  newMatrix._11 = c;
  newMatrix._12 = s;
  newMatrix._21 = -s;
  newMatrix._22 = c;

  return newMatrix;
}

template <>
Matrix4x4 MatrixDouble::operator*(const Matrix4x4& aMatrix) const {
  Matrix4x4 resultMatrix;

  resultMatrix._11 = this->_11 * aMatrix._11 + this->_12 * aMatrix._21;
  resultMatrix._12 = this->_11 * aMatrix._12 + this->_12 * aMatrix._22;
  resultMatrix._13 = this->_11 * aMatrix._13 + this->_12 * aMatrix._23;
  resultMatrix._14 = this->_11 * aMatrix._14 + this->_12 * aMatrix._24;

  resultMatrix._21 = this->_21 * aMatrix._11 + this->_22 * aMatrix._21;
  resultMatrix._22 = this->_21 * aMatrix._12 + this->_22 * aMatrix._22;
  resultMatrix._23 = this->_21 * aMatrix._13 + this->_22 * aMatrix._23;
  resultMatrix._24 = this->_21 * aMatrix._14 + this->_22 * aMatrix._24;

  resultMatrix._31 = aMatrix._31;
  resultMatrix._32 = aMatrix._32;
  resultMatrix._33 = aMatrix._33;
  resultMatrix._34 = aMatrix._34;

  resultMatrix._41 =
      this->_31 * aMatrix._11 + this->_32 * aMatrix._21 + aMatrix._41;
  resultMatrix._42 =
      this->_31 * aMatrix._12 + this->_32 * aMatrix._22 + aMatrix._42;
  resultMatrix._43 =
      this->_31 * aMatrix._13 + this->_32 * aMatrix._23 + aMatrix._43;
  resultMatrix._44 =
      this->_31 * aMatrix._14 + this->_32 * aMatrix._24 + aMatrix._44;

  return resultMatrix;
}

// Intersect the polygon given by aPoints with the half space induced by
// aPlaneNormal and return the resulting polygon. The returned points are
// stored in aDestBuffer, and its meaningful subspan is returned.
template <typename F>
Span<Point4DTyped<UnknownUnits, F>> IntersectPolygon(
    Span<Point4DTyped<UnknownUnits, F>> aPoints,
    const Point4DTyped<UnknownUnits, F>& aPlaneNormal,
    Span<Point4DTyped<UnknownUnits, F>> aDestBuffer) {
  if (aPoints.Length() < 1 || aDestBuffer.Length() < 1) {
    return {};
  }

  size_t nextIndex = 0;  // aDestBuffer[nextIndex] is the next emitted point.

  // Iterate over the polygon edges. In each iteration the current edge
  // is the edge from *prevPoint to point. If the two end points lie on
  // different sides of the plane, we have an intersection. Otherwise,
  // the edge is either completely "inside" the half-space created by
  // the clipping plane, and we add curPoint, or it is completely
  // "outside", and we discard curPoint. This loop can create duplicated
  // points in the polygon.
  const auto* prevPoint = &aPoints[aPoints.Length() - 1];
  F prevDot = aPlaneNormal.DotProduct(*prevPoint);
  for (const auto& curPoint : aPoints) {
    F curDot = aPlaneNormal.DotProduct(curPoint);

    if ((curDot >= 0.0) != (prevDot >= 0.0)) {
      // An intersection with the clipping plane has been detected.
      // Interpolate to find the intersecting curPoint and emit it.
      F t = -prevDot / (curDot - prevDot);
      aDestBuffer[nextIndex++] = curPoint * t + *prevPoint * (1.0 - t);
      if (nextIndex >= aDestBuffer.Length()) {
        break;
      }
    }

    if (curDot >= 0.0) {
      // Emit any source points that are on the positive side of the
      // clipping plane.
      aDestBuffer[nextIndex++] = curPoint;
      if (nextIndex >= aDestBuffer.Length()) {
        break;
      }
    }

    prevPoint = &curPoint;
    prevDot = curDot;
  }

  return aDestBuffer.To(nextIndex);
}

template Span<Point4DTyped<UnknownUnits, Float>> IntersectPolygon(
    Span<Point4DTyped<UnknownUnits, Float>> aPoints,
    const Point4DTyped<UnknownUnits, Float>& aPlaneNormal,
    Span<Point4DTyped<UnknownUnits, Float>> aDestBuffer);
template Span<Point4DTyped<UnknownUnits, Double>> IntersectPolygon(
    Span<Point4DTyped<UnknownUnits, Double>> aPoints,
    const Point4DTyped<UnknownUnits, Double>& aPlaneNormal,
    Span<Point4DTyped<UnknownUnits, Double>> aDestBuffer);

}  // namespace gfx
}  // namespace mozilla