通過自定義控件展示矩陣的幾何變換

轉載請註明出處:http://blog.csdn.net/ljmingcom304/article/details/49302395
本文出自:【梁敬明的博客】

1.矩陣表示二維圖形

    在二維空間中,可以通過x1x2...xny1y2...yn 表示一個圖形,其中xnyn 是頂點座標。如下圖三角形ABC用矩陣表示爲133131
座標

2.矩陣的幾何變換

    矩陣的基本幾何變換都是相對於原點或座標軸進行平移、旋轉、縮放、錯切等。
    在操作矩陣的幾何變換前首先需要科普下齊次座標法,所謂的齊次座標法就是將n維的向量用n+1維來表示,主要用計算矩陣的幾何變換,例如平移、旋轉、縮放等。例如:將點A(1,1)用矩陣的二維行向量表示爲[1,1],採用齊次座標法用矩陣的三維行向量表示爲[1,1,1]。所以三角形ABC用矩陣也可以表示爲133131111
    矩陣的幾何變換的計算方式爲:變換前圖形矩陣×變換矩陣1×變換矩陣2…=變換後圖形矩陣(當一個矩陣進行多次幾何變換時,只需要連乘即可)。假設一個三角形三個頂點座標分別爲(x1,y1),(x2,y2),(x3,y3) ,用圖形矩陣表示爲x1x2x3y1y2y3111 ,若其變換矩陣爲a1a2a3b1b2b3111 ,那麼變換後的結果爲(x1,y1),(x2,y2),(x3,y3) ,用圖形矩陣表示爲x1x2x3y1y2y3111 ×a1a2a3b1b2b3111 =a1x1+a2y1+a3a1x2+a2y2+a3a1x3+a2y3+a3b1x1+b2y1+b3b1x2+b2y2+b3b1x3+b2y3+b3x1+y1+1x2+y2+1x3+y3+1

public class MatrixUtils {

    /**
     * 多矩陣相乘
     * @param arrays 變換前圖形矩陣
     * @param arg 變換矩陣
     * @return 變換後圖形矩陣
     */
    public static float[][] matrixsMcl(float[][] arrays, float[][]... args) {
        float[][] results = arrays;
        for (float[][] arg : args) {
            results = matrixMcl(results, arg);
        }
        return results;
    }

    /**
     * 兩矩陣相乘
     * @param arrays 變換前圖形矩陣
     * @param arg 變換矩陣
     * @return 變換後圖形矩陣
     */
    private static float[][] matrixMcl(float[][] arrays, float[][] arg) {
        int i, j, k;
        float[][] results = new float[arrays.length][];
        for (i = 0; i < arrays.length; i++) {
            float[] array = arrays[i];
            results[i] = new float[array.length];
            float[][] newArg = transposeF(arg);
            for (j = 0; j < newArg.length; j++) {
                float[] newArgJ = newArg[j];
                int count = 0;
                for (k = 0; k < newArgJ.length; k++) {
                    count += array[k] * newArgJ[k];
                }
                results[i][j] = count;
            }
        }
        return results;
    }

    /**
     * 矩陣轉置:所有元素繞着一條從第1行第1列元素出發的右下方45度的射線作鏡面反轉
     * @param arrays 轉置前矩陣
     * @return 轉置後矩陣
     */
    private static float[][] transposeF(float[][] arrays) {
        int i, j;
        float[][] newArrays = new float[arrays[0].length][arrays.length];
        for (i = 0; i < arrays.length; i++) {
            for (j = 0; j < arrays[i].length; j++) {
                newArrays[j][i] = arrays[i][j]; // 轉置核心
            }
        }
        return newArrays;
    }

    ......
}
##2.1.平移變換     平移變換:圖形中的每一個點在給定方向上移動相同距離所進行的變換。     二維圖形的平移變換矩陣爲T=10m01n001 ,其中mn 分別表示xy 方向的平移量。
![平移](https://img-blog.csdn.net/20151021160844879)
public class MatrixUtils {

    ......

    /** 平移 */
    public static Point[] translate(float x, float y, Point... ps) {
        for (Point point : ps) {
            float[][] points = { { point.x, point.y, 1f } };
            float[][] translate = { { 1, 0, 0 }, { 0, 1, 1 }, { x, y, 1 } };

            //矩陣相乘
            float[][] results = matrixMcl(points, translate);
            point.x = (int) results[0][0];
            point.y = (int) results[0][1];
        }
        return ps;
    }

}
##2.2.縮放變換     縮放變換:圖形以固定點爲中心,按相同比例進行放大或縮小所進行的變換。     二維圖形的以原點爲中心縮放變換矩陣爲T=m000n0001 ,其中mn 分別表示xy 方向縮放的比例係數。
![縮放](https://img-blog.csdn.net/20151021160912756)
public class MatrixUtils {

    ......

    /** 縮放 */
    public static Point[] scale(float x, float y, Point... ps) {
        for (Point point : ps) {
            float[][] points = { { point.x, point.y, 1 } };
            float[][] scale = { { x, 0, 0 }, { 0, y, 1 }, { 0, 0, 1 } };

            //矩陣相乘
            float[][] results = matrixMcl(points, scale);
            point.x = (int) results[0][0];
            point.y = (int) results[0][1];
        }
        return ps;
    }

}
##2.3.對稱變換     縮放變換:圖形中的每一個點關於某個點或某條線進行對稱後所得的變換。     二維圖形的關於原點或直線對稱變換矩陣爲T=ab0de0001 ,其中若b=d=0a=1e1 時,圖形關於y軸對稱; 若b=d=0a=1e1 時,圖形關於x軸對稱;若bd1ae0 時,圖形關於直線y=x對稱;若bd1ae0 時,圖形關於直線y=-x對稱。
![對稱](https://img-blog.csdn.net/20151021160934656)
public class MatrixUtils {

    ......

    /** X軸對稱 */
    public static Point[] projectX(Point... ps) {
        for (Point point : ps) {
            float[][] points = { { point.x, point.y, 1 } };
            float[][] project = { { 1, 0, 0 }, { 0, -1, 0 }, { 0, 0, 1 } };

            //矩陣相乘
            float[][] results = matrixMcl(points, project);
            point.x = (int) results[0][0];
            point.y = (int) results[0][1];
        }
        return ps;
    }

    /** Y軸對稱 */
    public static Point[] projectY(Point... ps) {
        for (Point point : ps) {
            float[][] points = { { point.x, point.y, 1 } };
            float[][] project = { { -1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };

            //矩陣相乘
            float[][] results = matrixMcl(points, project);
            point.x = (int) results[0][0];
            point.y = (int) results[0][1];
        }
        return ps;
    }

    /** 原點對稱 */
    public static Point[] projectP(Point... ps) {
        for (Point point : ps) {
            float[][] points = { { point.x, point.y, 1 } };
            float[][] scale = { { -1, 0, 0 }, { 0, -1, 0 }, { 0, 0, 1 } };

            //矩陣相乘
            float[][] results = matrixMcl(points, scale);
            point.x = (int) results[0][0];
            point.y = (int) results[0][1];
        }
        return ps;
    }

}
##2.4.旋轉變換     旋轉變換:圖形中的中的每一個點按相同的方向、相同的旋轉角度旋轉所進行的變換。     二維圖形的關於原點的旋轉變換矩陣爲T=cosθsinθ0sinθcosθ0001 ,其中θ 表示圖形中各點繞原點旋轉的角度,當旋轉角度爲正時表示逆時針旋轉,旋轉角度爲負時表示順時針旋轉。
![旋轉](https://img-blog.csdn.net/20151021160955944)
public class MatrixUtils {

    ......

    /** 旋轉 */
    public static Point[] rotate(float angle, Point... ps) {
        for (Point point : ps) {
            float cos = (float) Math.cos(angle);
            float sin = (float) Math.sin(angle);
            float[][] points = { { point.x, point.y, 1 } };
            float[][] rotate = { { cos, -sin, 0 }, { sin, cos, 1 }, { 0, 0, 1 } };

            //矩陣相乘
            float[][] results = matrixMcl(points, rotate);
            point.x = (int) results[0][0];
            point.y = (int) results[0][1];
        }
        return ps;
    }

}
##2.5.錯切變換     錯切變換:圖形中的各點在某一方向上的座標保持不變,在另一方向上的座標進行線性變換,或在兩座標方向的座標都進行的線性變換。     二維圖形的錯切變換矩陣爲T=1b0d10001 ,其中若d=0,b0 時,圖形沿x軸方向作錯切位移;若d0,b=0 時,圖形沿y軸方向作錯切位移; 若d0,b0 時,圖形沿x軸和y軸兩個方向作錯切位移。
![錯切](https://img-blog.csdn.net/20151021161021735)
public class MatrixUtils {

    ......

    /** 錯切 */
    public static Point[] skew(float x, float y, Point... ps) {
        for (Point point : ps) {
            float[][] points = { { point.x, point.y, 1 } };
            float[][] skew = { { 1, y, 0 }, { x, 1, 0 }, { 0, 0, 1 } };
            float[][] results = matrixMcl(points, skew);
            point.x = (int) results[0][0];
            point.y = (int) results[0][1];
        }
        return ps;
    }

}
#3.通過自定義控件實現矩陣幾何變換     首先在自定義控件中基於控制點繪製一個初始的矩形方框:
public class MatrixView extends View {

    private Paint paint;
    private Path path;
    private Point[] ps;
    private int w;
    private int h;
    private Point p1;
    private Point p2;
    private Point p3;
    private Point p4;

    public MatrixView(Context context) {
        this(context, null);

    }

    public MatrixView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MatrixView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        paint = new Paint();
        paint.setAntiAlias(true);//抗鋸齒使邊緣更加平滑
        paint.setStrokeWidth(3);//邊框寬度
        paint.setStyle(Style.STROKE);
        paint.setColor(Color.RED);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        this.w = w;//控件的寬度
        this.h = h;//控件的高度
        p1 = new Point(-w / 2, -h / 4);
        p2 = new Point(0, -h / 4);
        p3 = new Point(0, 0);
        p4 = new Point(-w / 2, 0);
        ps = new Point[] { p1, p2, p3, p4 };
        path = new Path();
    }

    /**通過Path路徑繪製一個矩形*/
    private void toShape(boolean isClose, Path path, Point... ps) {
        for (int i = 0; i < ps.length; i++) {
            if (i == 0) {
                path.moveTo(ps[0].x, ps[0].y);
            } else {
                path.lineTo(ps[i].x, ps[i].y);
            }
        }
        if (isClose)
            path.close();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 繪製初始圖形
        toShape(true, path, ps);
        //將畫布的原點移動到屏幕的中央
        canvas.translate(w / 2, h / 2);
        canvas.drawPath(path, paint);
        path.reset();
    }
    ......
}
    然後基於控制點對當前View進行幾何變換:
public class MatrixView extends View {
    ......
    /**還原*/
    public void clear() {
        ps = new Point[] { new Point(-w / 2, -h / 4), new Point(0, -h / 4),
                new Point(0, 0), new Point(-w / 2, 0) };
        invalidate();
    }

    /** 平移 */
    public void translate() {
        ps = MatrixUtils.translate(200, 200, ps);
        invalidate();
    }

    /** 錯切 */
    public void skew() {
        ps = MatrixUtils.skew(-0.5f, 0, ps);
        invalidate();
    }

    /** 縮放 */
    public void scale() {
        ps = MatrixUtils.scale(0.5f, 0.5f, ps);
        invalidate();
    }

    /** 旋轉 */
    public void rotate() {
        ps = MatrixUtils.rotate(-(float) Math.PI / 4, ps);
        invalidate();
    }

    /** X軸對稱 */
    public void projectX() {
        ps = MatrixUtils.projectX(ps);
        invalidate();
    }

    /** Y軸對稱 */
    public void projectY() {
        ps = MatrixUtils.projectY(ps);
        invalidate();
    }

    /** 原點對稱 */
    public void projectP() {
        ps = MatrixUtils.projectP(ps);
        invalidate();
    }
}

    最後顯示效果圖:
變換

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章