com.agateau.pixelwheels.map.Warper.java Source code

Java tutorial

Introduction

Here is the source code for com.agateau.pixelwheels.map.Warper.java

Source

/*
 * Copyright 2017 Aurlien Gteau <mail@agateau.com>
 *
 * This file is part of Pixel Wheels.
 *
 * Tiny Wheels is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License 3 as published by the Free
 * Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.agateau.pixelwheels.map;

import com.badlogic.gdx.math.Vector2;

/**
 * A Java port of [Warper.cs][1] by Simon Nilsson. Original class summary:
 *
 * This class is responsible for transforming a 2D-coordinate on a source rectangle onto a that of a destination rectangle.
 * The transformation is linear and will not take into account bent or curved surfaces (the transformations are affine!).
 * This is based on the work done by Johnny Lee and can be found here: http://johnnylee.net/projects/wii/
 *
 * [1]: https://github.com/simphax/Touchmote/blob/master/WiiTUIO/Input/WiiProvider/Warper.cs
 *
 */
class Warper {
    float[] srcX = new float[4];
    float[] srcY = new float[4];
    float[] dstX = new float[4];
    float[] dstY = new float[4];
    float[] srcMat = new float[16];
    float[] dstMat = new float[16];
    float[] warpMat = new float[16];
    boolean dirty;

    public Warper() {
        setIdentity();
    }

    public void setIdentity() {
        setSource(0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f);
        setDestination(0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f);
        computeWarp();
    }

    public void setSource(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) {
        srcX[0] = x0;
        srcY[0] = y0;
        srcX[1] = x1;
        srcY[1] = y1;
        srcX[2] = x2;
        srcY[2] = y2;
        srcX[3] = x3;
        srcY[3] = y3;
        dirty = true;
    }

    public void setDestination(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) {
        dstX[0] = x0;
        dstY[0] = y0;
        dstX[1] = x1;
        dstY[1] = y1;
        dstX[2] = x2;
        dstY[2] = y2;
        dstX[3] = x3;
        dstY[3] = y3;
        dirty = true;
    }

    public void computeWarp() {
        computeQuadToSquare(srcX[0], srcY[0], srcX[1], srcY[1], srcX[2], srcY[2], srcX[3], srcY[3], srcMat);
        computeSquareToQuad(dstX[0], dstY[0], dstX[1], dstY[1], dstX[2], dstY[2], dstX[3], dstY[3], dstMat);
        multMats(srcMat, dstMat, warpMat);
        dirty = false;
    }

    public void multMats(float[] srcMat, float[] dstMat, float[] resMat) {
        // DSTDO/CBB: could be faster, but not called often enough to matter
        for (int r = 0; r < 4; r++) {
            int ri = r * 4;
            for (int c = 0; c < 4; c++) {
                resMat[ri + c] = (srcMat[ri] * dstMat[c] + srcMat[ri + 1] * dstMat[c + 4]
                        + srcMat[ri + 2] * dstMat[c + 8] + srcMat[ri + 3] * dstMat[c + 12]);
            }
        }
    }

    @SuppressWarnings("UnnecessaryLocalVariable")
    public void computeSquareToQuad(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3,
            float[] mat) {

        float dx1 = x1 - x2, dy1 = y1 - y2;
        float dx2 = x3 - x2, dy2 = y3 - y2;
        float sx = x0 - x1 + x2 - x3;
        float sy = y0 - y1 + y2 - y3;
        float g = (sx * dy2 - dx2 * sy) / (dx1 * dy2 - dx2 * dy1);
        float h = (dx1 * sy - sx * dy1) / (dx1 * dy2 - dx2 * dy1);
        float a = x1 - x0 + g * x1;
        float b = x3 - x0 + h * x3;
        float c = x0;
        float d = y1 - y0 + g * y1;
        float e = y3 - y0 + h * y3;
        float f = y0;

        mat[0] = a;
        mat[1] = d;
        mat[2] = 0;
        mat[3] = g;
        mat[4] = b;
        mat[5] = e;
        mat[6] = 0;
        mat[7] = h;
        mat[8] = 0;
        mat[9] = 0;
        mat[10] = 1;
        mat[11] = 0;
        mat[12] = c;
        mat[13] = f;
        mat[14] = 0;
        mat[15] = 1;
    }

    public void computeQuadToSquare(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3,
            float[] mat) {
        computeSquareToQuad(x0, y0, x1, y1, x2, y2, x3, y3, mat);

        // invert through adjoint

        float a = mat[0], d = mat[1], /* ignore */ g = mat[3];
        float b = mat[4], e = mat[5], /* 3rd col*/ h = mat[7];
        /* ignore 3rd row */
        float c = mat[12], f = mat[13];

        float A = e - f * h;
        float B = c * h - b;
        float C = b * f - c * e;
        float D = f * g - d;
        float E = a - c * g;
        float F = c * d - a * f;
        float G = d * h - e * g;
        float H = b * g - a * h;
        float I = a * e - b * d;

        // Probably unnecessary since 'I' is also scaled by the determinant,
        //   and 'I' scales the homogeneous coordinate, which, in turn,
        //   scales the X,Y coordinates.
        // Determinant  =   a * (e - f * h) + b * (f * g - d) + c * (d * h - e * g);
        float idet = 1.0f / (a * A + b * D + c * G);

        mat[0] = A * idet;
        mat[1] = D * idet;
        mat[2] = 0;
        mat[3] = G * idet;
        mat[4] = B * idet;
        mat[5] = E * idet;
        mat[6] = 0;
        mat[7] = H * idet;
        mat[8] = 0;
        mat[9] = 0;
        mat[10] = 1;
        mat[11] = 0;
        mat[12] = C * idet;
        mat[13] = F * idet;
        mat[14] = 0;
        mat[15] = I * idet;
    }

    private final Vector2 mOutput = new Vector2();

    public Vector2 warp(float srcX, float srcY) {
        if (dirty)
            computeWarp();
        Warper.warp(warpMat, srcX, srcY, mOutput);
        return mOutput;
    }

    private static void warp(float[] mat, float srcX, float srcY, Vector2 output) {
        final float result0 = srcX * mat[0] + srcY * mat[4] + mat[12];
        final float result1 = srcX * mat[1] + srcY * mat[5] + mat[13];
        final float result3 = srcX * mat[3] + srcY * mat[7] + mat[15];
        output.set(result0 / result3, result1 / result3);
    }

}