Search code examples
javaquaternionsgoogle-project-tangorajawali

Project Tango: Determine whether an IntersectionPointPlaneModelPair is aligned with Gravity


Spoiler Alert: I am not sure whether or not I am using Quaternions in the correct way.

I have an IntersectionPointPlaneModelPair pair from using the TangoSupport.fitPlaneModelNearClick(...) method. I would now like to find out whether or not this plane is aligned with Gravity (more or less). My approach was to create a Quaternion (Rajawali) from the pair.planeModel and another from ScenePoseCalculator.TANGO_WORLD_UP and a rotation of 0.0, multiply them and determine the angle between the original and the product:

IntersectionPointPlaneModelPair pair= TangoSupport.fitPlaneModelNearClick(...);
double x = 0.05; // subject to change
double[] p = pair.planeModel;
Quaternion plane = new Quaternion(p[0], p[1], p[2], p[3]);
plane.normalize();
Quaternion gravity = Quaternion(ScenePoseCalculator.TANGO_WORLD_UP.clone(), 0.0);
Quaternion product = plane.multiply(gravity);
if (plane.angleBetween(product) > x){
     ...   
}

However, this does not work, because the product turned out to be identical to the plane. Thanks in advance!


Solution

  • I found out, that I was having a wrong understanding of Quaternions. I also found this formula for angle calculation of planes. Therefore I changed my implementation to be the following:

    Edit: New Answer (old answer below)

    private boolean isAlignedWithGravity(IntersectionPointPlaneModelPair candidate,
                                             TangoPoseData devicePose, double maxDeviation) {
        Matrix4 adfTdevice = ScenePoseCalculator.tangoPoseToMatrix(devicePose);
        Vector3 gravityVector = ScenePoseCalculator.TANGO_WORLD_UP.clone();
        adfTdevice.clone().multiply(mExtrinsics.getDeviceTDepthCamera()).inverse().
                rotateVector(gravityVector);
    
        double[] gravity = new double[]{gravityVector.x, gravityVector.y, gravityVector.z};
        double angle = VectorUtilities.getAngleBetweenVectors(candidate.planeModel, gravity);
        // vectors should be perpendicular => 90° => PI / 2 in radians
        double target = Math.PI / 2; 
        return (Math.abs(target - angle) <= maxDeviation);
    }
    

    And in a class VectorUtilities:

    /**
     * Calculates the angle between two planes according to http://www.wolframalpha
     * .com/input/?i=dihedral+angle
     */
    public static double getAngleBetweenVectors(double[] a, double[] b) {
        double numerator = 0;
        for (int i = 0; i < Math.min(a.length, b.length); i++){
            numerator += a[i] * b[i];
        }
        double denominator = getLength(a) * getLength(b);
        return Math.acos(numerator / denominator);
    }
    
    
    public static double getLength(double[] vector) {
        double sum = 0.0;
        for (double dimension : vector) {
            sum += (dimension * dimension);
        }
        return Math.sqrt(sum);
    }
    


    Old Answer

    private boolean isAlignedWithGravity(IntersectionPointPlaneModelPair pair,
                                         TangoPoseData devicePose) {
        Matrix4 adfTdevice = ScenePoseCalculator.tangoPoseToMatrix(devicePose);
        Vector3 gravityVector = ScenePoseCalculator.TANGO_WORLD_UP.clone();
        adfTdevice.clone().multiply(mExtrinsics.getDeviceTDepthCamera()).inverse().
                rotateVector(gravityVector);
    
        double[] gravity = new double[]{gravityVector.x, gravityVector.y, gravityVector.z};
        double angle = getAngleBetweenPlanes(pair.planeModel, gravity);
        Log.d(TAG, "angle: " + angle);
        if (angle < 0.1) {
            return false;
        }
        return true;
    }
    
    /**
     * Calculates the angle between two planes according to http://mathworld.wolfram
     * .com/DihedralAngle.html
     */
    private double getAngleBetweenPlanes(double[] a, double[] b) {
        double numerator = Math.abs(a[0] * b[0] + a[1] * b[1] + a[2] * b[2]);
        double aFactor = Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
        double bFactor = Math.sqrt(b[0] * b[0] + b[1] * b[1] + b[2] * b[2]);
        double denumerator = aFactor * bFactor;
        double result = Math.acos(numerator / denumerator);
        return result;
    }