/* * Copyright (C) 2010-2016 JPEXS, All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. */ package com.jpexs.decompiler.flash.exporters.commonshape; import com.jpexs.decompiler.flash.types.MATRIX; import java.awt.geom.AffineTransform; /** * * @author JPEXS */ public final class Matrix implements Cloneable { public double scaleX = 1; public double scaleY = 1; public double rotateSkew0; public double rotateSkew1; public double translateX; public double translateY; public static Matrix getScaleInstance(double scale) { Matrix mat = new Matrix(); mat.scale(scale); return mat; } public static Matrix getTranslateInstance(double x, double y) { Matrix mat = new Matrix(); mat.translate(x, y); return mat; } public Matrix() { scaleX = 1; scaleY = 1; } public Matrix(MATRIX matrix) { if (matrix == null) { matrix = new MATRIX(); } translateX = matrix.translateX; translateY = matrix.translateY; if (matrix.hasScale) { scaleX = matrix.getScaleXFloat(); scaleY = matrix.getScaleYFloat(); } else { scaleX = 1; scaleY = 1; } if (matrix.hasRotate) { rotateSkew0 = matrix.getRotateSkew0Float(); rotateSkew1 = matrix.getRotateSkew1Float(); } } public Matrix(AffineTransform transform) { this(); if (transform != null) { scaleX = transform.getScaleX(); rotateSkew1 = transform.getShearX(); translateX = transform.getTranslateX(); rotateSkew0 = transform.getShearY(); scaleY = transform.getScaleY(); translateY = transform.getTranslateY(); } } @Override public Matrix clone() { try { Matrix mat = (Matrix) super.clone(); return mat; } catch (CloneNotSupportedException ex) { throw new RuntimeException(); } } public Point transform(double x, double y) { Point result = new Point( scaleX * x + rotateSkew1 * y + translateX, rotateSkew0 * x + scaleY * y + translateY); return result; } public Point transform(Point point) { return transform(point.x, point.y); } public Point deltaTransform(double x, double y) { Point result = new Point( scaleX * x + rotateSkew1 * y, rotateSkew0 * x + scaleY * y); return result; } public Point deltaTransform(Point point) { return deltaTransform(point.x, point.y); } public java.awt.Point deltaTransform(java.awt.Point point) { Point p = deltaTransform(point.x, point.y); return new java.awt.Point((int) p.x, (int) p.y); } public java.awt.Point transform(java.awt.Point point) { Point p = transform(point.x, point.y); return new java.awt.Point((int) p.x, (int) p.y); } public ExportRectangle transform(ExportRectangle rect) { double minX = Double.MAX_VALUE; double minY = Double.MAX_VALUE; double maxX = Double.MIN_VALUE; double maxY = Double.MIN_VALUE; Point point; point = transform(rect.xMin, rect.yMin); if (point.x < minX) { minX = point.x; } if (point.x > maxX) { maxX = point.x; } if (point.y < minY) { minY = point.y; } if (point.y > maxY) { maxY = point.y; } point = transform(rect.xMax, rect.yMin); if (point.x < minX) { minX = point.x; } if (point.x > maxX) { maxX = point.x; } if (point.y < minY) { minY = point.y; } if (point.y > maxY) { maxY = point.y; } point = transform(rect.xMin, rect.yMax); if (point.x < minX) { minX = point.x; } if (point.x > maxX) { maxX = point.x; } if (point.y < minY) { minY = point.y; } if (point.y > maxY) { maxY = point.y; } point = transform(rect.xMax, rect.yMax); if (point.x < minX) { minX = point.x; } if (point.x > maxX) { maxX = point.x; } if (point.y < minY) { minY = point.y; } if (point.y > maxY) { maxY = point.y; } return new ExportRectangle(minX, minY, maxX, maxY); } public void translate(double x, double y) { translateX = scaleX * x + rotateSkew1 * y + translateX; translateY = rotateSkew0 * x + scaleY * y + translateY; } public void scale(double factor) { scaleX *= factor; scaleY *= factor; rotateSkew0 *= factor; rotateSkew1 *= factor; } public Matrix concatenate(Matrix m) { Matrix result = new Matrix(); result.scaleX = scaleX * m.scaleX + rotateSkew1 * m.rotateSkew0; result.rotateSkew0 = rotateSkew0 * m.scaleX + scaleY * m.rotateSkew0; result.rotateSkew1 = scaleX * m.rotateSkew1 + rotateSkew1 * m.scaleY; result.scaleY = rotateSkew0 * m.rotateSkew1 + scaleY * m.scaleY; result.translateX = scaleX * m.translateX + rotateSkew1 * m.translateY + translateX; result.translateY = rotateSkew0 * m.translateX + scaleY * m.translateY + translateY; return result; } public Matrix preConcatenate(Matrix m) { Matrix result = new Matrix(); result.scaleX = m.scaleX * scaleX + m.rotateSkew1 * rotateSkew0; result.rotateSkew0 = m.rotateSkew0 * scaleX + m.scaleY * rotateSkew0; result.rotateSkew1 = m.scaleX * rotateSkew1 + m.rotateSkew1 * scaleY; result.scaleY = m.rotateSkew0 * rotateSkew1 + m.scaleY * scaleY; result.translateX = m.scaleX * translateX + m.rotateSkew1 * translateY + m.translateX; result.translateY = m.rotateSkew0 * translateX + m.scaleY * translateY + m.translateY; return result; } public AffineTransform toTransform() { AffineTransform transform = new AffineTransform(scaleX, rotateSkew0, rotateSkew1, scaleY, translateX, translateY); return transform; } public String getSvgTransformationString(double translateDivisor, double unitDivisor) { double translateX = roundPixels400(this.translateX / translateDivisor); double translateY = roundPixels400(this.translateY / translateDivisor); double rotateSkew0 = roundPixels400(this.rotateSkew0 / unitDivisor); double rotateSkew1 = roundPixels400(this.rotateSkew1 / unitDivisor); double scaleX = roundPixels400(this.scaleX / unitDivisor); double scaleY = roundPixels400(this.scaleY / unitDivisor); return "matrix(" + scaleX + ", " + rotateSkew0 + ", " + rotateSkew1 + ", " + scaleY + ", " + translateX + ", " + translateY + ")"; } public static String[] parseSvgNumberList(String params) { while (params.contains(" ")) { params = params.replaceAll(" ", " "); } params = params.trim(); params = params.replace(", ", ","); params = params.replace(" ", ","); String[] args = params.split(","); return args; } public static Matrix parseSvgMatrix(String transformStr, double translateDivisor, double unitDivisor) { Matrix ret = new Matrix(); while (transformStr != null && transformStr.length() > 0) { String funcName = transformStr.split("\\(")[0]; transformStr = transformStr.substring(funcName.length() + 1); String params = transformStr.split("\\)")[0]; transformStr = transformStr.substring(params.length() + 1).trim(); String[] args = parseSvgNumberList(params); funcName = funcName.trim(); switch (funcName) { case "matrix": if (args.length == 6) { double scaleX = Double.parseDouble(args[0].trim()); double rotateSkew0 = Double.parseDouble(args[1].trim()); double rotateSkew1 = Double.parseDouble(args[2].trim()); double scaleY = Double.parseDouble(args[3].trim()); double translateX = Double.parseDouble(args[4].trim()); double translateY = Double.parseDouble(args[5].trim()); Matrix result = new Matrix(); result.translateX = translateX; result.translateY = translateY; result.rotateSkew0 = rotateSkew0; result.rotateSkew1 = rotateSkew1; result.scaleX = scaleX; result.scaleY = scaleY; ret = ret.concatenate(result); } break; case "translate": if (args.length == 1 || args.length == 2) { double translateX = Double.parseDouble(args[0].trim()); double translateY = 0; if (args.length == 2) { translateY = Double.parseDouble(args[1].trim()); } Matrix result = new Matrix(); result.translateX = translateX; result.translateY = translateY; ret = ret.concatenate(result); } break; case "scale": if (args.length == 1 || args.length == 2) { double scaleX = Double.parseDouble(args[0].trim()); double scaleY = scaleX; if (args.length == 2) { scaleY = Double.parseDouble(args[1].trim()); } Matrix result = new Matrix(); result.scaleX = scaleX; result.scaleY = scaleY; ret = ret.concatenate(result); } break; case "skewX": if (args.length == 1) { double angle = Double.parseDouble(args[0].trim()) * Math.PI / 180; Matrix result = new Matrix(); result.rotateSkew1 = Math.tan(angle); ret = ret.concatenate(result); } break; case "skewY": if (args.length == 1) { double angle = Double.parseDouble(args[0].trim()) * Math.PI / 180; Matrix result = new Matrix(); result.rotateSkew0 = Math.tan(angle); ret = ret.concatenate(result); } break; case "rotate": if (args.length == 1 || args.length == 3) { double rotateAngle = Double.parseDouble(args[0].trim()); double tx = 0; double ty = 0; if (args.length > 1) { tx = Double.parseDouble(args[1].trim()); ty = Double.parseDouble(args[2].trim()); } double angleRad = -rotateAngle * Math.PI / 180; Matrix result = new Matrix(); result.rotateSkew0 = -Math.sin(angleRad); result.rotateSkew1 = Math.sin(angleRad); result.scaleX = Math.cos(angleRad); result.scaleY = Math.cos(angleRad); result = result.preConcatenate(getTranslateInstance(tx, ty)) .concatenate(getTranslateInstance(-tx, -ty)); ret = ret.concatenate(result); } break; } } ret.translateX *= translateDivisor; ret.translateY *= translateDivisor; ret.rotateSkew0 *= unitDivisor; ret.rotateSkew1 *= unitDivisor; ret.scaleX *= unitDivisor; ret.scaleY *= unitDivisor; return ret; } private double roundPixels400(double pixels) { return Math.round(pixels * 10000) / 10000.0; } @Override public String toString() { return "[Matrix scale:" + scaleX + "," + scaleY + ", rotate:" + rotateSkew0 + "," + rotateSkew1 + ", translate:" + translateX + "," + translateY + "]"; } public Matrix inverse() { double a = scaleX; double b = rotateSkew1; double tx = translateX; double c = rotateSkew0; double d = scaleY; double ty = translateY; double det = a * d - b * c; double a2 = d / det; double b2 = -b / det; double tx2 = (b * ty - tx * d) / det; double c2 = -c / det; double d2 = a / det; double ty2 = (tx * c - a * ty) / det; Matrix ret = new Matrix(); ret.scaleX = a2; ret.rotateSkew0 = c2; ret.rotateSkew1 = b2; ret.scaleY = d2; ret.translateX = tx2; ret.translateY = ty2; return ret; } public double getTotalSkewAngleX() { Point px = deltaTransform(new Point(0, 1)); return ((180 / Math.PI) * Math.atan2(px.y, px.x) - 90); } public double getTotalSkewAngleY() { Point py = deltaTransform(new Point(1, 0)); return ((180 / Math.PI) * Math.atan2(py.y, py.x)); } public double getTotalScaleX() { return Math.sqrt(scaleX * scaleX + rotateSkew0 * rotateSkew0); } public double getTotalScaleY() { return Math.sqrt(rotateSkew1 * rotateSkew1 + scaleY * scaleY); } private int fromFloat(double f) { return (int) (f * (1 << 16)); } public MATRIX toMATRIX() { MATRIX result = new MATRIX(); result.translateX = (int) translateX; result.translateY = (int) translateY; result.hasRotate = true; result.hasScale = true; result.scaleX = fromFloat(scaleX); result.scaleY = fromFloat(scaleY); result.rotateSkew0 = fromFloat(rotateSkew0); result.rotateSkew1 = fromFloat(rotateSkew1); return result; } }