通过自定义控件展示矩阵的几何变换

转载请注明出处: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();
    }
}

    最后显示效果图:
变换

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