Revert "Revert "add rotate to SkM44""

Fix: update the generator code itself for the vec --> ptr change

This reverts commit 44aa1ab584e481089a4eb4fcaab6726cc52f250a.

Change-Id: Idfec2b42239429e58501ca2ba108ec852891e237
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/266575
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Mike Reed <reed@google.com>
diff --git a/include/private/SkM44.h b/include/private/SkM44.h
index 1a75b78..3f1ecb0 100644
--- a/include/private/SkM44.h
+++ b/include/private/SkM44.h
@@ -44,8 +44,8 @@
     SkScalar dot(const SkV3& v) const { return Dot(*this, v); }
     SkV3 cross(const SkV3& v) const { return Cross(*this, v); }
 
-    const float* vec() const { return &x; }
-    float* vec() { return &x; }
+    const float* ptr() const { return &x; }
+    float* ptr() { return &x; }
 };
 
 struct SkV4 {
@@ -68,10 +68,18 @@
     }
     friend SkV4 operator*(SkScalar s, const SkV4& v) { return v*s; }
 
-    const float* vec() const { return &x; }
-    float* vec() { return &x; }
+    const float* ptr() const { return &x; }
+    float* ptr() { return &x; }
 };
 
+/**
+ *  4x4 matrix used by SkCanvas and other parts of Skia.
+ *
+ *  Skia assumes a right-handed coordinate system:
+ *      +X goes to the right
+ *      +Y goes down
+ *      +Z goes into the screen (away from the viewer)
+ */
 class SkM44 {
 public:
     SkM44(const SkM44& src) = default;
@@ -151,6 +159,12 @@
                      0, 0, 0, 1);
     }
 
+    static SkM44 Rotate(SkV3 axis, SkScalar radians) {
+        SkM44 m(kUninitialized_Constructor);
+        m.setRotate(axis, radians);
+        return m;
+    }
+
     bool operator==(const SkM44& other) const;
     bool operator!=(const SkM44& other) const {
         return !(other == *this);
@@ -209,7 +223,7 @@
     }
     void setCol(int i, const SkV4& v) {
         SkASSERT(i >= 0 && i <= 3);
-        memcpy(&fMat[i*4], v.vec(), sizeof(v));
+        memcpy(&fMat[i*4], v.ptr(), sizeof(v));
     }
 
     SkM44& setIdentity() {
@@ -233,6 +247,34 @@
                            0, 0, 0, 1);
     }
 
+    /**
+     *  Set this matrix to rotate about the specified unit-length axis vector,
+     *  by an angle specified by its sin() and cos().
+     *
+     *  This does not attempt to verify that axis.length() == 1 or that the sin,cos values
+     *  are correct.
+     */
+    SkM44& setRotateUnitSinCos(SkV3 axis, SkScalar sinAngle, SkScalar cosAngle);
+
+    /**
+     *  Set this matrix to rotate about the specified unit-length axis vector,
+     *  by an angle specified in radians.
+     *
+     *  This does not attempt to verify that axis.length() == 1.
+     */
+    SkM44& setRotateUnit(SkV3 axis, SkScalar radians) {
+        return this->setRotateUnitSinCos(axis, SkScalarSin(radians), SkScalarCos(radians));
+    }
+
+    /**
+     *  Set this matrix to rotate about the specified axis vector,
+     *  by an angle specified in radians.
+     *
+     *  Note: axis is not assumed to be unit-length, so it will be normalized internally.
+     *        If axis is already unit-length, call setRotateAboutUnitRadians() instead.
+     */
+    SkM44& setRotate(SkV3 axis, SkScalar radians);
+
     SkM44& setConcat16(const SkM44& a, const SkScalar colMajor[16]);
 
     SkM44& setConcat(const SkM44& a, const SkM44& b) {
@@ -262,7 +304,7 @@
     SkV4 operator*(const SkV4& v) const {
         return this->map(v.x, v.y, v.z, v.w);
     }
-    SkV3 operator*(const SkV3& v) const {
+    SkV3 operator*(SkV3 v) const {
         auto v4 = this->map(v.x, v.y, v.z, 0);
         return {v4.x, v4.y, v4.z};
     }
diff --git a/src/core/SkM44.cpp b/src/core/SkM44.cpp
index d490036..73971cd 100644
--- a/src/core/SkM44.cpp
+++ b/src/core/SkM44.cpp
@@ -256,6 +256,32 @@
     return trans;
 }
 
+SkM44& SkM44::setRotateUnitSinCos(SkV3 axis, SkScalar sinAngle, SkScalar cosAngle) {
+    // Taken from "Essential Mathematics for Games and Interactive Applications"
+    //             James M. Van Verth and Lars M. Bishop -- third edition
+    SkScalar x = axis.x;
+    SkScalar y = axis.y;
+    SkScalar z = axis.z;
+    SkScalar c = cosAngle;
+    SkScalar s = sinAngle;
+    SkScalar t = 1 - c;
+
+    return this->set44(t*x*x + c,   t*x*y - s*z, t*x*z + s*y, 0,
+                       t*x*y + s*z, t*y*y + c,   t*y*z - s*x, 0,
+                       t*x*z - s*y, t*y*z + s*x, t*z*z + c,   0,
+                       0,           0,           0,           1);
+}
+
+SkM44& SkM44::setRotate(SkV3 axis, SkScalar radians) {
+    SkScalar len = axis.length();
+    if (len > 0 && SkScalarIsFinite(len)) {
+        this->setRotateUnit(axis * (SK_Scalar1 / len), radians);
+    } else {
+        this->setIdentity();
+    }
+    return *this;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkM44::dump() const {
diff --git a/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp
index 9357443..357b1c0 100644
--- a/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp
+++ b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp
@@ -63,7 +63,7 @@
             const SkV4& vValue = _outer.v;
             if (vPrev != (vValue)) {
                 vPrev = vValue;
-                pdman.set4fv(vVar, 1, vValue.vec());
+                pdman.set4fv(vVar, 1, vValue.ptr());
             }
         }
     }
diff --git a/src/sksl/SkSLCPPUniformCTypes.cpp b/src/sksl/SkSLCPPUniformCTypes.cpp
index 4c550c3..061ad55 100644
--- a/src/sksl/SkSLCPPUniformCTypes.cpp
+++ b/src/sksl/SkSLCPPUniformCTypes.cpp
@@ -162,7 +162,7 @@
         "{SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}"),                   // default value
 
     REGISTER(Layout::CType::kSkV4, { "half4", "float4", "double4" },
-        "${pdman}.set4fv(${uniform}, 1, ${var}.vec())",                            // to gpu
+        "${pdman}.set4fv(${uniform}, 1, ${var}.ptr())",                            // to gpu
         "SkV4{SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}",                // default value
         "${oldVar} != (${newVar})"),                                               // dirty check
 
diff --git a/tests/Matrix44Test.cpp b/tests/Matrix44Test.cpp
index dcad75b..3290e0e 100644
--- a/tests/Matrix44Test.cpp
+++ b/tests/Matrix44Test.cpp
@@ -1058,3 +1058,57 @@
     REPORTER_ASSERT(reporter, (c0 - r0 == SkV4{c0.x-r0.x, c0.y-r0.y, c0.z-r0.z, c0.w-r0.w}));
     REPORTER_ASSERT(reporter, (c0 * r0 == SkV4{c0.x*r0.x, c0.y*r0.y, c0.z*r0.z, c0.w*r0.w}));
 }
+
+DEF_TEST(M44_rotate, reporter) {
+    const SkV3 x = {1, 0, 0},
+               y = {0, 1, 0},
+               z = {0, 0, 1};
+
+    // We have radians version of setRotateAbout methods, but even with our best approx
+    // for PI, sin(SK_ScalarPI) != 0, so to make the comparisons in the unittest clear,
+    // I'm using the variants that explicitly take the sin,cos values.
+
+    struct {
+        SkScalar sinAngle, cosAngle;
+        SkV3 aboutAxis;
+        SkV3 expectedX, expectedY, expectedZ;
+    } recs[] = {
+        { 0, 1,    x,   x, y, z},    // angle = 0
+        { 0, 1,    y,   x, y, z},    // angle = 0
+        { 0, 1,    z,   x, y, z},    // angle = 0
+
+        { 0,-1,    x,   x,-y,-z},    // angle = 180
+        { 0,-1,    y,  -x, y,-z},    // angle = 180
+        { 0,-1,    z,  -x,-y, z},    // angle = 180
+
+        // Skia coordinate system is right-handed
+
+        { 1, 0,    x,   x, z,-y},    // angle = 90
+        { 1, 0,    y,  -z, y, x},    // angle = 90
+        { 1, 0,    z,   y,-x, z},    // angle = 90
+
+        {-1, 0,    x,   x,-z, y},    // angle = -90
+        {-1, 0,    y,   z, y,-x},    // angle = -90
+        {-1, 0,    z,  -y, x, z},    // angle = -90
+    };
+
+    for (const auto& r : recs) {
+        SkM44 m(SkM44::kNaN_Constructor);
+        m.setRotateUnitSinCos(r.aboutAxis, r.sinAngle, r.cosAngle);
+
+        auto mx = m * x;
+        auto my = m * y;
+        auto mz = m * z;
+        REPORTER_ASSERT(reporter, mx == r.expectedX);
+        REPORTER_ASSERT(reporter, my == r.expectedY);
+        REPORTER_ASSERT(reporter, mz == r.expectedZ);
+
+        // flipping the axis-of-rotation should flip the results
+        mx = m * -x;
+        my = m * -y;
+        mz = m * -z;
+        REPORTER_ASSERT(reporter, mx == -r.expectedX);
+        REPORTER_ASSERT(reporter, my == -r.expectedY);
+        REPORTER_ASSERT(reporter, mz == -r.expectedZ);
+    }
+}