All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.hansolo.fx.geometry.transform.AffineBase Maven / Gradle / Ivy

There is a newer version: 1.0.5
Show newest version
/*
 * Copyright (c) 2017 by Gerrit Grunwald
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package eu.hansolo.fx.geometry.transform;

import eu.hansolo.fx.geometry.BaseBounds;
import eu.hansolo.fx.geometry.Path;
import eu.hansolo.fx.geometry.RectBounds;
import eu.hansolo.fx.geometry.Rectangle;
import eu.hansolo.fx.geometry.Shape;
import eu.hansolo.fx.geometry.tools.NonInvertibleTransformException;
import eu.hansolo.fx.geometry.tools.Point;


public abstract class AffineBase extends BaseTransform {
    protected static final int APPLY_IDENTITY  = 0;
    protected static final int APPLY_TRANSLATE = 1;
    protected static final int APPLY_SCALE     = 2;
    protected static final int APPLY_SHEAR     = 4;
    protected static final int APPLY_2D_MASK = (APPLY_TRANSLATE | APPLY_SCALE | APPLY_SHEAR);
    protected static final int APPLY_2D_DELTA_MASK = (APPLY_SCALE | APPLY_SHEAR);
    protected static final int HI_SHIFT = 4;
    protected static final int HI_IDENTITY = APPLY_IDENTITY << HI_SHIFT;
    protected static final int HI_TRANSLATE = APPLY_TRANSLATE << HI_SHIFT;
    protected static final int HI_SCALE = APPLY_SCALE << HI_SHIFT;
    protected static final int HI_SHEAR = APPLY_SHEAR << HI_SHIFT;
    protected double mxx;
    protected double myx;
    protected double mxy;
    protected double myy;
    protected double mxt;
    protected double myt;
    protected transient int state;
    protected transient int type;
    private static final int rot90conversion[] = {
        /* IDENTITY => */        APPLY_SHEAR,
        /* TRANSLATE (TR) => */  APPLY_SHEAR | APPLY_TRANSLATE,
        /* SCALE (SC) => */      APPLY_SHEAR,
        /* SC | TR => */         APPLY_SHEAR | APPLY_TRANSLATE,
        /* SHEAR (SH) => */      APPLY_SCALE,
        /* SH | TR => */         APPLY_SCALE | APPLY_TRANSLATE,
        /* SH | SC => */         APPLY_SHEAR | APPLY_SCALE,
        /* SH | SC | TR => */    APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE,
        };


    protected static void stateError() { throw new InternalError("missing case in transform state switch"); }

    protected void updateState() {
        if (mxy == 0.0 && myx == 0.0) {
            if (mxx == 1.0 && myy == 1.0) {
                if (mxt == 0.0 && myt == 0.0) {
                    state = APPLY_IDENTITY;
                    type = TYPE_IDENTITY;
                } else {
                    state = APPLY_TRANSLATE;
                    type = TYPE_TRANSLATION;
                }
            } else {
                if (mxt == 0.0 && myt == 0.0) {
                    state = APPLY_SCALE;
                } else {
                    state = (APPLY_SCALE | APPLY_TRANSLATE);
                }
                type = TYPE_UNKNOWN;
            }
        } else {
            if (mxx == 0.0 && myy == 0.0) {
                if (mxt == 0.0 && myt == 0.0) {
                    state = APPLY_SHEAR;
                } else {
                    state = (APPLY_SHEAR | APPLY_TRANSLATE);
                }
            } else {
                if (mxt == 0.0 && myt == 0.0) {
                    state = (APPLY_SHEAR | APPLY_SCALE);
                } else {
                    state = (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE);
                }
            }
            type = TYPE_UNKNOWN;
        }
    }
    
    public int getType() {
        if (type == TYPE_UNKNOWN) {
            updateState();
            if (type == TYPE_UNKNOWN) { type = calculateType(); }
        }
        return type;
    }

    protected int calculateType() {
        int ret = TYPE_IDENTITY;
        boolean sgn0, sgn1;
        switch (state & APPLY_2D_MASK) {
            default:
                stateError();
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
                ret |= TYPE_TRANSLATION;
            case (APPLY_SHEAR | APPLY_SCALE):
                if (mxx * mxy + myx * myy != 0) {
                    ret |= TYPE_GENERAL_TRANSFORM;
                    break;
                }
                sgn0 = (mxx >= 0.0);
                sgn1 = (myy >= 0.0);
                if (sgn0 == sgn1) {
                    if (mxx != myy || mxy != -myx) {
                        ret |= (TYPE_GENERAL_ROTATION | TYPE_GENERAL_SCALE);
                    } else if (mxx * myy - mxy * myx != 1.0) {
                        ret |= (TYPE_GENERAL_ROTATION | TYPE_UNIFORM_SCALE);
                    } else {
                        ret |= TYPE_GENERAL_ROTATION;
                    }
                } else {
                    if (mxx != -myy || mxy != myx) {
                        ret |= (TYPE_GENERAL_ROTATION |
                                TYPE_FLIP |
                                TYPE_GENERAL_SCALE);
                    } else if (mxx * myy - mxy * myx != 1.0) {
                        ret |= (TYPE_GENERAL_ROTATION |
                                TYPE_FLIP |
                                TYPE_UNIFORM_SCALE);
                    } else {
                        ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP);
                    }
                }
                break;
            case (APPLY_SHEAR | APPLY_TRANSLATE):
                ret |= TYPE_TRANSLATION;
            case (APPLY_SHEAR):
                sgn0 = (mxy >= 0.0);
                sgn1 = (myx >= 0.0);
                if (sgn0 != sgn1) {
                    if (mxy != -myx) {
                        ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE);
                    } else if (mxy != 1.0 && mxy != -1.0) {
                        ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE);
                    } else {
                        ret |= TYPE_QUADRANT_ROTATION;
                    }
                } else {
                    if (mxy == myx) {
                        ret |= (TYPE_QUADRANT_ROTATION | TYPE_FLIP | TYPE_UNIFORM_SCALE);
                    } else {
                        ret |= (TYPE_QUADRANT_ROTATION | TYPE_FLIP | TYPE_GENERAL_SCALE);
                    }
                }
                break;
            case (APPLY_SCALE | APPLY_TRANSLATE):
                ret |= TYPE_TRANSLATION;
            case (APPLY_SCALE):
                sgn0 = (mxx >= 0.0);
                sgn1 = (myy >= 0.0);
                if (sgn0 == sgn1) {
                    if (sgn0) {
                        if (mxx == myy) {
                            ret |= TYPE_UNIFORM_SCALE;
                        } else {
                            ret |= TYPE_GENERAL_SCALE;
                        }
                    } else {
                        if (mxx != myy) {
                            ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE);
                        } else if (mxx != -1.0) {
                            ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE);
                        } else {
                            ret |= TYPE_QUADRANT_ROTATION;
                        }
                    }
                } else {
                    if (mxx == -myy) {
                        if (mxx == 1.0 || mxx == -1.0) {
                            ret |= TYPE_FLIP;
                        } else {
                            ret |= (TYPE_FLIP | TYPE_UNIFORM_SCALE);
                        }
                    } else {
                        ret |= (TYPE_FLIP | TYPE_GENERAL_SCALE);
                    }
                }
                break;
            case (APPLY_TRANSLATE):
                ret |= TYPE_TRANSLATION;
                break;
            case (APPLY_IDENTITY):
                break;
        }
        return ret;
    }

    @Override public double getMxx() { return mxx; }
    @Override public double getMyy() { return myy; }
    @Override public double getMxy() { return mxy; }
    @Override public double getMyx() { return myx; }
    @Override public double getMxt() { return mxt; }
    @Override public double getMyt() { return myt; }

    public boolean isIdentity() {
        return (state == APPLY_IDENTITY || (getType() == TYPE_IDENTITY));
    }

    @Override public boolean isTranslateOrIdentity() {
        return (state <= APPLY_TRANSLATE || (getType() <= TYPE_TRANSLATION));
    }

    public double getDeterminant() {
        switch (state) {
            default                                           : stateError();
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
            case (APPLY_SHEAR | APPLY_SCALE)                  : return mxx * myy - mxy * myx;
            case (APPLY_SHEAR | APPLY_TRANSLATE)              :
            case (APPLY_SHEAR)                                : return -(mxy * myx);
            case (APPLY_SCALE | APPLY_TRANSLATE)              :
            case (APPLY_SCALE)                                : return mxx * myy;
            case (APPLY_TRANSLATE)                            :
            case (APPLY_IDENTITY)                             : return 1.0;
        }
    }
    
    public void setToIdentity() {
        mxx = myy = 1.0;
        myx = mxy = mxt = myt = 0.0;
        state = APPLY_IDENTITY;
        type = TYPE_IDENTITY;
    }

    public void setTransform(double mxx, double myx, double mxy, double myy, double mxt, double myt) {
        this.mxx = mxx;
        this.myx = myx;
        this.mxy = mxy;
        this.myy = myy;
        this.mxt = mxt;
        this.myt = myt;
        updateState();
    }

    public void setToShear(double shx, double shy) {
        mxx = 1.0;
        mxy = shx;
        myx = shy;
        myy = 1.0;
        mxt = 0.0;
        myt = 0.0;
        if (shx != 0.0 || shy != 0.0) {
            state = (APPLY_SHEAR | APPLY_SCALE);
            type = TYPE_UNKNOWN;
        } else {
            state = APPLY_IDENTITY;
            type = TYPE_IDENTITY;
        }
    }

    public Point transform(Point pt) { return transform(pt, pt); }
    public Point transform(Point ptSrc, Point ptDst) {
        if (ptDst == null) { ptDst = new Point(); }
        double x = ptSrc.x;
        double y = ptSrc.y;
        switch (state & APPLY_2D_MASK) {
            default:
                stateError();
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
                ptDst.set((x * mxx + y * mxy + mxt), (x * myx + y * myy + myt));
                return ptDst;
            case (APPLY_SHEAR | APPLY_SCALE):
                ptDst.set((x * mxx + y * mxy), (x * myx + y * myy));
                return ptDst;
            case (APPLY_SHEAR | APPLY_TRANSLATE):
                ptDst.set((y * mxy + mxt), (x * myx + myt));
                return ptDst;
            case (APPLY_SHEAR):
                ptDst.set((y * mxy), (x * myx));
                return ptDst;
            case (APPLY_SCALE | APPLY_TRANSLATE):
                ptDst.set((x * mxx + mxt), (y * myy + myt));
                return ptDst;
            case (APPLY_SCALE):
                ptDst.set((x * mxx), (y * myy));
                return ptDst;
            case (APPLY_TRANSLATE):
                ptDst.set((x + mxt), (y + myt));
                return ptDst;
            case (APPLY_IDENTITY):
                ptDst.set(x, y);
                return ptDst;
        }
    }
    public BaseBounds transform(BaseBounds src, BaseBounds dst) {
        return transform2DBounds((RectBounds)src, (RectBounds)dst);
    }
    public void transform(Rectangle src, Rectangle dst) {
        switch (state & APPLY_2D_MASK) {
            default:
                stateError();
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
            case (APPLY_SHEAR | APPLY_SCALE):
            case (APPLY_SHEAR | APPLY_TRANSLATE):
            case (APPLY_SHEAR):
            case (APPLY_SCALE | APPLY_TRANSLATE):
            case (APPLY_SCALE):
                RectBounds b = new RectBounds(src);
                b = (RectBounds) transform(b, b);
                dst.setBounds(b);
                return;
            case (APPLY_TRANSLATE):
                Translate.transform(src, dst, mxt, myt);
                return;
            case (APPLY_IDENTITY):
                if (dst != src) { dst.setBounds(src); }
                return;
        }
    }
    public void transform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) {
        double Mxx, Mxy, Mxt, Myx, Myy, Myt;    // For caching
        switch (state & APPLY_2D_MASK) {
            default:
                stateError();
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
                Mxx = mxx; Mxy = mxy; Mxt = mxt;
                Myx = myx; Myy = myy; Myt = myt;
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++];
                    double y = srcPts[srcOff++];
                    dstPts[dstOff++] = (Mxx * x + Mxy * y + Mxt);
                    dstPts[dstOff++] = (Myx * x + Myy * y + Myt);
                }
                return;
            case (APPLY_SHEAR | APPLY_SCALE):
                Mxx = mxx; Mxy = mxy;
                Myx = myx; Myy = myy;
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++];
                    double y = srcPts[srcOff++];
                    dstPts[dstOff++] = (Mxx * x + Mxy * y);
                    dstPts[dstOff++] = (Myx * x + Myy * y);
                }
                return;
            case (APPLY_SHEAR | APPLY_TRANSLATE):
                Mxy = mxy; Mxt = mxt;
                Myx = myx; Myt = myt;
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++];
                    dstPts[dstOff++] = (Mxy * srcPts[srcOff++] + Mxt);
                    dstPts[dstOff++] = (Myx * x + Myt);
                }
                return;
            case (APPLY_SHEAR):
                Mxy = mxy; Myx = myx;
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++];
                    dstPts[dstOff++] = (Mxy * srcPts[srcOff++]);
                    dstPts[dstOff++] = (Myx * x);
                }
                return;
            case (APPLY_SCALE | APPLY_TRANSLATE):
                Mxx = mxx; Mxt = mxt;
                Myy = myy; Myt = myt;
                while (--numPts >= 0) {
                    dstPts[dstOff++] = (Mxx * srcPts[srcOff++] + Mxt);
                    dstPts[dstOff++] = (Myy * srcPts[srcOff++] + Myt);
                }
                return;
            case (APPLY_SCALE):
                Mxx = mxx; Myy = myy;
                while (--numPts >= 0) {
                    dstPts[dstOff++] = (Mxx * srcPts[srcOff++]);
                    dstPts[dstOff++] = (Myy * srcPts[srcOff++]);
                }
                return;
            case (APPLY_TRANSLATE):
                Mxt = mxt; Myt = myt;
                while (--numPts >= 0) {
                    dstPts[dstOff++] = (srcPts[srcOff++] + Mxt);
                    dstPts[dstOff++] = (srcPts[srcOff++] + Myt);
                }
                return;
            case (APPLY_IDENTITY):
                while (--numPts >= 0) {
                    dstPts[dstOff++] = (srcPts[srcOff++]);
                    dstPts[dstOff++] = (srcPts[srcOff++]);
                }
                return;
        }
    }

    private BaseBounds transform2DBounds(RectBounds src, RectBounds dst) {
        switch (state & APPLY_2D_MASK) {
            default:
                stateError();
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
            case (APPLY_SHEAR | APPLY_SCALE):
                double x1 = src.getMinX();
                double y1 = src.getMinY();
                double x2 = src.getMaxX();
                double y2 = src.getMaxY();
                dst.setBoundsAndSort((x1 * mxx + y1 * mxy), (x1 * myx + y1 * myy), (x2 * mxx + y2 * mxy), (x2 * myx + y2 * myy));
                dst.add((x1 * mxx + y2 * mxy), (x1 * myx + y2 * myy));
                dst.add((x2 * mxx + y1 * mxy), (x2 * myx + y1 * myy));
                dst.setBounds((dst.getMinX() + mxt), (dst.getMinY() + myt), (dst.getMaxX() + mxt), (dst.getMaxY() + myt));
                break;
            case (APPLY_SHEAR | APPLY_TRANSLATE):
                dst.setBoundsAndSort((src.getMinY() * mxy + mxt), (src.getMinX() * myx + myt), (src.getMaxY() * mxy + mxt), (src.getMaxX() * myx + myt));
                break;
            case (APPLY_SHEAR):
                dst.setBoundsAndSort((src.getMinY() * mxy), (src.getMinX() * myx), (src.getMaxY() * mxy), (src.getMaxX() * myx));
                break;
            case (APPLY_SCALE | APPLY_TRANSLATE):
                dst.setBoundsAndSort((src.getMinX() * mxx + mxt), (src.getMinY() * myy + myt), (src.getMaxX() * mxx + mxt), (src.getMaxY() * myy + myt));
                break;
            case (APPLY_SCALE):
                dst.setBoundsAndSort((src.getMinX() * mxx), (src.getMinY() * myy), (src.getMaxX() * mxx), (src.getMaxY() * myy));
                break;
            case (APPLY_TRANSLATE):
                dst.setBounds((src.getMinX() + mxt), (src.getMinY() + myt), (src.getMaxX() + mxt), (src.getMaxY() + myt));
                break;
            case (APPLY_IDENTITY):
                if (src != dst) { dst.setBounds(src); }
                break;
        }
        return dst;
    }

    public void deltaTransform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) {
        doTransform(srcPts, srcOff, dstPts, dstOff, numPts, (this.state & APPLY_2D_DELTA_MASK));
    }

    private void doTransform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts, int thestate) {
        double Mxx, Mxy, Mxt, Myx, Myy, Myt;    // For caching
        if (dstPts == srcPts && dstOff > srcOff && dstOff < srcOff + numPts * 2) {
            System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
            srcOff = dstOff;
        }
        switch (thestate) {
            default:
                stateError();
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
                Mxx = mxx; Mxy = mxy; Mxt = mxt;
                Myx = myx; Myy = myy; Myt = myt;
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++];
                    double y = srcPts[srcOff++];
                    dstPts[dstOff++] = (Mxx * x + Mxy * y + Mxt);
                    dstPts[dstOff++] = (Myx * x + Myy * y + Myt);
                }
                return;
            case (APPLY_SHEAR | APPLY_SCALE):
                Mxx = mxx; Mxy = mxy;
                Myx = myx; Myy = myy;
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++];
                    double y = srcPts[srcOff++];
                    dstPts[dstOff++] = (Mxx * x + Mxy * y);
                    dstPts[dstOff++] = (Myx * x + Myy * y);
                }
                return;
            case (APPLY_SHEAR | APPLY_TRANSLATE):
                Mxy = mxy; Mxt = mxt;
                Myx = myx; Myt = myt;
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++];
                    dstPts[dstOff++] = (Mxy * srcPts[srcOff++] + Mxt);
                    dstPts[dstOff++] = (Myx * x + Myt);
                }
                return;
            case (APPLY_SHEAR):
                Mxy = mxy; Myx = myx;
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++];
                    dstPts[dstOff++] = (Mxy * srcPts[srcOff++]);
                    dstPts[dstOff++] = (Myx * x);
                }
                return;
            case (APPLY_SCALE | APPLY_TRANSLATE):
                Mxx = mxx; Mxt = mxt;
                Myy = myy; Myt = myt;
                while (--numPts >= 0) {
                    dstPts[dstOff++] = (Mxx * srcPts[srcOff++] + Mxt);
                    dstPts[dstOff++] = (Myy * srcPts[srcOff++] + Myt);
                }
                return;
            case (APPLY_SCALE):
                Mxx = mxx; Myy = myy;
                while (--numPts >= 0) {
                    dstPts[dstOff++] = (Mxx * srcPts[srcOff++]);
                    dstPts[dstOff++] = (Myy * srcPts[srcOff++]);
                }
                return;
            case (APPLY_TRANSLATE):
                Mxt = mxt; Myt = myt;
                while (--numPts >= 0) {
                    dstPts[dstOff++] = (srcPts[srcOff++] + Mxt);
                    dstPts[dstOff++] = (srcPts[srcOff++] + Myt);
                }
                return;
            case (APPLY_IDENTITY):
                if (srcPts != dstPts || srcOff != dstOff) {
                    System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
                }
                return;
        }
    }

    public Point inverseTransform(Point ptSrc, Point ptDst) throws NonInvertibleTransformException {
        if (ptDst == null) { ptDst = new Point(); }
        double x = ptSrc.x;
        double y = ptSrc.y;
        switch (state) {
            default:
                stateError();
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
                x -= mxt;
                y -= myt;
            case (APPLY_SHEAR | APPLY_SCALE):
                double det = mxx * myy - mxy * myx;
                if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NonInvertibleTransformException("Determinant is " + det); }
                ptDst.set(((x * myy - y * mxy) / det), ((y * mxx - x * myx) / det));
                return ptDst;
            case (APPLY_SHEAR | APPLY_TRANSLATE):
                x -= mxt;
                y -= myt;
            case (APPLY_SHEAR):
                if (mxy == 0.0 || myx == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                ptDst.set((y / myx), (x / mxy));
                return ptDst;
            case (APPLY_SCALE | APPLY_TRANSLATE):
                x -= mxt;
                y -= myt;
            case (APPLY_SCALE):
                if (mxx == 0.0 || myy == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                ptDst.set((x / mxx), (y / myy));
                return ptDst;
            case (APPLY_TRANSLATE):
                ptDst.set((x - mxt), (y - myt));
                return ptDst;
            case (APPLY_IDENTITY):
                ptDst.set(x, y);
                return ptDst;
        }
    }

    private BaseBounds inversTransform2DBounds(RectBounds src, RectBounds dst) throws NonInvertibleTransformException {
        switch (state) {
            default:
                stateError();
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
            case (APPLY_SHEAR | APPLY_SCALE):
                double det = mxx * myy - mxy * myx;
                if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NonInvertibleTransformException("Determinant is " + det); }
                double x1 = src.getMinX() - mxt;
                double y1 = src.getMinY() - myt;
                double x2 = src.getMaxX() - mxt;
                double y2 = src.getMaxY() - myt;
                dst.setBoundsAndSort(((x1 * myy - y1 * mxy) / det),
                                     ((y1 * mxx - x1 * myx) / det),
                                     ((x2 * myy - y2 * mxy) / det),
                                     ((y2 * mxx - x2 * myx) / det));
                dst.add(((x2 * myy - y1 * mxy) / det),
                        ((y1 * mxx - x2 * myx) / det));
                dst.add(((x1 * myy - y2 * mxy) / det),
                        ((y2 * mxx - x1 * myx) / det));
                return dst;
            case (APPLY_SHEAR | APPLY_TRANSLATE):
                if (mxy == 0.0 || myx == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                dst.setBoundsAndSort(((src.getMinY() - myt) / myx),
                                     ((src.getMinX() - mxt) / mxy),
                                     ((src.getMaxY() - myt) / myx),
                                     ((src.getMaxX() - mxt) / mxy));
                break;
            case (APPLY_SHEAR):
                if (mxy == 0.0 || myx == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                dst.setBoundsAndSort((src.getMinY() / myx),
                                     (src.getMinX() / mxy),
                                     (src.getMaxY() / myx),
                                     (src.getMaxX() / mxy));
                break;
            case (APPLY_SCALE | APPLY_TRANSLATE):
                if (mxx == 0.0 || myy == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                dst.setBoundsAndSort(((src.getMinX() - mxt) / mxx),
                                     ((src.getMinY() - myt) / myy),
                                     ((src.getMaxX() - mxt) / mxx),
                                     ((src.getMaxY() - myt) / myy));
                break;
            case (APPLY_SCALE):
                if (mxx == 0.0 || myy == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                dst.setBoundsAndSort((src.getMinX() / mxx),
                                     (src.getMinY() / myy),
                                     (src.getMaxX() / mxx),
                                     (src.getMaxY() / myy));
                break;
            case (APPLY_TRANSLATE):
                dst.setBounds((src.getMinX() - mxt),
                              (src.getMinY() - myt),
                              (src.getMaxX() - mxt),
                              (src.getMaxY() - myt));
                break;
            case (APPLY_IDENTITY):
                if (dst != src) { dst.setBounds((RectBounds) src); }
                break;
        }
        return dst;
    }

    public BaseBounds inverseTransform(BaseBounds src, BaseBounds dst) throws NonInvertibleTransformException {
        return inversTransform2DBounds((RectBounds)src, (RectBounds)dst);
    }
    public void inverseTransform(Rectangle src, Rectangle dst) throws NonInvertibleTransformException {
        switch (state) {
            default:
                stateError();
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
            case (APPLY_SHEAR | APPLY_SCALE):
            case (APPLY_SHEAR | APPLY_TRANSLATE):
            case (APPLY_SHEAR):
            case (APPLY_SCALE | APPLY_TRANSLATE):
            case (APPLY_SCALE):
                RectBounds b = new RectBounds(src);
                b = (RectBounds) inverseTransform(b, b);
                dst.setBounds(b);
                return;
            case (APPLY_TRANSLATE):
                Translate.transform(src, dst, -mxt, -myt);
                return;
            case (APPLY_IDENTITY):
                if (dst != src) { dst.setBounds(src); }
                return;
        }
    }
    public void inverseTransform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) throws NonInvertibleTransformException {
        double Mxx, Mxy, Mxt, Myx, Myy, Myt;    // For caching
        double det;
        if (dstPts == srcPts && dstOff > srcOff && dstOff < srcOff + numPts * 2) {
            System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
            srcOff = dstOff;
        }
        switch (state) {
            default:
                stateError();
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
                Mxx = mxx; Mxy = mxy; Mxt = mxt;
                Myx = myx; Myy = myy; Myt = myt;
                det = Mxx * Myy - Mxy * Myx;
                if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NonInvertibleTransformException("Determinant is " + det);  }
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++] - Mxt;
                    double y = srcPts[srcOff++] - Myt;
                    dstPts[dstOff++] = (x * Myy - y * Mxy) / det;
                    dstPts[dstOff++] = (y * Mxx - x * Myx) / det;
                }
                return;
            case (APPLY_SHEAR | APPLY_SCALE):
                Mxx = mxx; Mxy = mxy;
                Myx = myx; Myy = myy;
                det = Mxx * Myy - Mxy * Myx;
                if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NonInvertibleTransformException("Determinant is " + det); }
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++];
                    double y = srcPts[srcOff++];
                    dstPts[dstOff++] = (x * Myy - y * Mxy) / det;
                    dstPts[dstOff++] = (y * Mxx - x * Myx) / det;
                }
                return;
            case (APPLY_SHEAR | APPLY_TRANSLATE):
                Mxy = mxy; Mxt = mxt;
                Myx = myx; Myt = myt;
                if (Mxy == 0.0 || Myx == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++] - Mxt;
                    dstPts[dstOff++] = (srcPts[srcOff++] - Myt) / Myx;
                    dstPts[dstOff++] = x / Mxy;
                }
                return;
            case (APPLY_SHEAR):
                Mxy = mxy; Myx = myx;
                if (Mxy == 0.0 || Myx == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++];
                    dstPts[dstOff++] = srcPts[srcOff++] / Myx;
                    dstPts[dstOff++] = x / Mxy;
                }
                return;
            case (APPLY_SCALE | APPLY_TRANSLATE):
                Mxx = mxx; Mxt = mxt;
                Myy = myy; Myt = myt;
                if (Mxx == 0.0 || Myy == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                while (--numPts >= 0) {
                    dstPts[dstOff++] = (srcPts[srcOff++] - Mxt) / Mxx;
                    dstPts[dstOff++] = (srcPts[srcOff++] - Myt) / Myy;
                }
                return;
            case (APPLY_SCALE):
                Mxx = mxx; Myy = myy;
                if (Mxx == 0.0 || Myy == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                while (--numPts >= 0) {
                    dstPts[dstOff++] = srcPts[srcOff++] / Mxx;
                    dstPts[dstOff++] = srcPts[srcOff++] / Myy;
                }
                return;
            case (APPLY_TRANSLATE):
                Mxt = mxt; Myt = myt;
                while (--numPts >= 0) {
                    dstPts[dstOff++] = srcPts[srcOff++] - Mxt;
                    dstPts[dstOff++] = srcPts[srcOff++] - Myt;
                }
                return;
            case (APPLY_IDENTITY):
                if (srcPts != dstPts || srcOff != dstOff) {
                    System.arraycopy(srcPts, srcOff, dstPts, dstOff,numPts * 2);
                }
                return;
        }
    }

    public void inverseDeltaTransform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) throws NonInvertibleTransformException {
        doInverseTransform(srcPts, srcOff, dstPts, dstOff, numPts, state & ~APPLY_TRANSLATE);
    }
    
    private void doInverseTransform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts, int thestate) throws NonInvertibleTransformException {
        double Mxx, Mxy, Mxt, Myx, Myy, Myt;    // For caching
        double det;
        if (dstPts == srcPts && dstOff > srcOff && dstOff < srcOff + numPts * 2) {
            System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
            srcOff = dstOff;
        }
        switch (thestate) {
            default:
                stateError();
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
                Mxx = mxx; Mxy = mxy; Mxt = mxt;
                Myx = myx; Myy = myy; Myt = myt;
                det = Mxx * Myy - Mxy * Myx;
                if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NonInvertibleTransformException("Determinant is " + det); }
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++] - Mxt;
                    double y = srcPts[srcOff++] - Myt;
                    dstPts[dstOff++] = ((x * Myy - y * Mxy) / det);
                    dstPts[dstOff++] = ((y * Mxx - x * Myx) / det);
                }
                return;
            case (APPLY_SHEAR | APPLY_SCALE):
                Mxx = mxx; Mxy = mxy;
                Myx = myx; Myy = myy;
                det = Mxx * Myy - Mxy * Myx;
                if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NonInvertibleTransformException("Determinant is " + det); }
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++];
                    double y = srcPts[srcOff++];
                    dstPts[dstOff++] = ((x * Myy - y * Mxy) / det);
                    dstPts[dstOff++] = ((y * Mxx - x * Myx) / det);
                }
                return;
            case (APPLY_SHEAR | APPLY_TRANSLATE):
                Mxy = mxy; Mxt = mxt;
                Myx = myx; Myt = myt;
                if (Mxy == 0.0 || Myx == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++] - Mxt;
                    dstPts[dstOff++] = ((srcPts[srcOff++] - Myt) / Myx);
                    dstPts[dstOff++] = (x / Mxy);
                }
                return;
            case (APPLY_SHEAR):
                Mxy = mxy; Myx = myx;
                if (Mxy == 0.0 || Myx == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                while (--numPts >= 0) {
                    double x = srcPts[srcOff++];
                    dstPts[dstOff++] = (srcPts[srcOff++] / Myx);
                    dstPts[dstOff++] = (x / Mxy);
                }
                return;
            case (APPLY_SCALE | APPLY_TRANSLATE):
                Mxx = mxx; Mxt = mxt;
                Myy = myy; Myt = myt;
                if (Mxx == 0.0 || Myy == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                while (--numPts >= 0) {
                    dstPts[dstOff++] = ((srcPts[srcOff++] - Mxt) / Mxx);
                    dstPts[dstOff++] = ((srcPts[srcOff++] - Myt) / Myy);
                }
                return;
            case (APPLY_SCALE):
                Mxx = mxx; Myy = myy;
                if (Mxx == 0.0 || Myy == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                while (--numPts >= 0) {
                    dstPts[dstOff++] = (srcPts[srcOff++] / Mxx);
                    dstPts[dstOff++] = (srcPts[srcOff++] / Myy);
                }
                return;
            case (APPLY_TRANSLATE):
                Mxt = mxt; Myt = myt;
                while (--numPts >= 0) {
                    dstPts[dstOff++] = (srcPts[srcOff++] - Mxt);
                    dstPts[dstOff++] = (srcPts[srcOff++] - Myt);
                }
                return;
            case (APPLY_IDENTITY):
                if (srcPts != dstPts || srcOff != dstOff) {
                    System.arraycopy(srcPts, srcOff, dstPts, dstOff,numPts * 2);
                }
                return;
        }
    }

    public Shape createTransformedShape(Shape s) {
        if (s == null) { return null; }
        return new Path(s, this);
    }

    public void translate(double tx, double ty) {
        switch (state) {
            default:
                stateError();
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
                mxt = tx * mxx + ty * mxy + mxt;
                myt = tx * myx + ty * myy + myt;
                if (mxt == 0.0 && myt == 0.0) {
                    state = APPLY_SHEAR | APPLY_SCALE;
                    if (type != TYPE_UNKNOWN) { type &= ~TYPE_TRANSLATION; }
                }
                return;
            case (APPLY_SHEAR | APPLY_SCALE):
                mxt = tx * mxx + ty * mxy;
                myt = tx * myx + ty * myy;
                if (mxt != 0.0 || myt != 0.0) {
                    state = APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE;
                    type |= TYPE_TRANSLATION;
                }
                return;
            case (APPLY_SHEAR | APPLY_TRANSLATE):
                mxt = ty * mxy + mxt;
                myt = tx * myx + myt;
                if (mxt == 0.0 && myt == 0.0) {
                    state = APPLY_SHEAR;
                    if (type != TYPE_UNKNOWN) { type &= ~TYPE_TRANSLATION;} }
                return;
            case (APPLY_SHEAR):
                mxt = ty * mxy;
                myt = tx * myx;
                if (mxt != 0.0 || myt != 0.0) {
                    state = APPLY_SHEAR | APPLY_TRANSLATE;
                    type |= TYPE_TRANSLATION;
                }
                return;
            case (APPLY_SCALE | APPLY_TRANSLATE):
                mxt = tx * mxx + mxt;
                myt = ty * myy + myt;
                if (mxt == 0.0 && myt == 0.0) {
                    state = APPLY_SCALE;
                    if (type != TYPE_UNKNOWN) { type &= ~TYPE_TRANSLATION; }
                }
                return;
            case (APPLY_SCALE):
                mxt = tx * mxx;
                myt = ty * myy;
                if (mxt != 0.0 || myt != 0.0) {
                    state = APPLY_SCALE | APPLY_TRANSLATE;
                    type |= TYPE_TRANSLATION;
                }
                return;
            case (APPLY_TRANSLATE):
                mxt = tx + mxt;
                myt = ty + myt;
                if (mxt == 0.0 && myt == 0.0) {
                    state = APPLY_IDENTITY;
                    type = TYPE_IDENTITY;
                }
                return;
            case (APPLY_IDENTITY):
                mxt = tx;
                myt = ty;
                if (tx != 0.0 || ty != 0.0) {
                    state = APPLY_TRANSLATE;
                    type = TYPE_TRANSLATION;
                }
                return;
        }
    }

    protected final void rotate90() {
        double M0 = mxx;
        mxx = mxy;
        mxy = -M0;
        M0 = myx;
        myx = myy;
        myy = -M0;
        int newstate = rot90conversion[this.state];
        if ((newstate & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE &&
            mxx == 1.0 && myy == 1.0)
        {
            newstate -= APPLY_SCALE;
        }
        this.state = newstate;
        type = TYPE_UNKNOWN;
    }
    protected final void rotate180() {
        mxx = -mxx;
        myy = -myy;
        int oldstate = this.state;
        if ((oldstate & (APPLY_SHEAR)) != 0) {
            // If there was a shear, then this rotation has no
            // effect on the state.
            mxy = -mxy;
            myx = -myx;
        } else {
            // No shear means the SCALE state may toggle when
            // m00 and m11 are negated.
            if (mxx == 1.0 && myy == 1.0) {
                this.state = oldstate & ~APPLY_SCALE;
            } else {
                this.state = oldstate | APPLY_SCALE;
            }
        }
        type = TYPE_UNKNOWN;
    }
    protected final void rotate270() {
        double M0 = mxx;
        mxx = -mxy;
        mxy = M0;
        M0 = myx;
        myx = -myy;
        myy = M0;
        int newstate = rot90conversion[this.state];
        if ((newstate & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE &&
            mxx == 1.0 && myy == 1.0)
        {
            newstate -= APPLY_SCALE;
        }
        this.state = newstate;
        type = TYPE_UNKNOWN;
    }

    public void rotate(double theta) {
        // assert(APPLY_3D was dealt with at a higher level)
        double sin = Math.sin(theta);
        if (sin == 1.0) {
            rotate90();
        } else if (sin == -1.0) {
            rotate270();
        } else {
            double cos = Math.cos(theta);
            if (cos == -1.0) {
                rotate180();
            } else if (cos != 1.0) {
                double M0, M1;
                M0 = mxx;
                M1 = mxy;
                mxx =  cos * M0 + sin * M1;
                mxy = -sin * M0 + cos * M1;
                M0 = myx;
                M1 = myy;
                myx =  cos * M0 + sin * M1;
                myy = -sin * M0 + cos * M1;
                updateState();
            }
        }
    }

    public void scale(double sx, double sy) {
        int mystate = this.state;
        // assert(APPLY_3D was dealt with at a higher level)
        switch (mystate) {
            default:
                stateError();
            /* NOTREACHED */
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
            case (APPLY_SHEAR | APPLY_SCALE):
                mxx *= sx;
                myy *= sy;
            /* NOBREAK */
            case (APPLY_SHEAR | APPLY_TRANSLATE):
            case (APPLY_SHEAR):
                mxy *= sy;
                myx *= sx;
                if (mxy == 0 && myx == 0) {
                    mystate &= APPLY_TRANSLATE;
                    if (mxx == 1.0 && myy == 1.0) {
                        this.type = (mystate == APPLY_IDENTITY
                                     ? TYPE_IDENTITY
                                     : TYPE_TRANSLATION);
                    } else {
                        mystate |= APPLY_SCALE;
                        this.type = TYPE_UNKNOWN;
                    }
                    this.state = mystate;
                }
                return;
            case (APPLY_SCALE | APPLY_TRANSLATE):
            case (APPLY_SCALE):
                mxx *= sx;
                myy *= sy;
                if (mxx == 1.0 && myy == 1.0) {
                    this.state = (mystate &= APPLY_TRANSLATE);
                    this.type = (mystate == APPLY_IDENTITY
                                 ? TYPE_IDENTITY
                                 : TYPE_TRANSLATION);
                } else {
                    this.type = TYPE_UNKNOWN;
                }
                return;
            case (APPLY_TRANSLATE):
            case (APPLY_IDENTITY):
                mxx = sx;
                myy = sy;
                if (sx != 1.0 || sy != 1.0) {
                    this.state = mystate | APPLY_SCALE;
                    this.type = TYPE_UNKNOWN;
                }
                return;
        }
    }

    public void shear(double shx, double shy) {
        int mystate = this.state;
        // assert(APPLY_3D was dealt with at a higher level)
        switch (mystate) {
            default:
                stateError();
            /* NOTREACHED */
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
            case (APPLY_SHEAR | APPLY_SCALE):
                double M0, M1;
                M0 = mxx;
                M1 = mxy;
                mxx = M0 + M1 * shy;
                mxy = M0 * shx + M1;

                M0 = myx;
                M1 = myy;
                myx = M0 + M1 * shy;
                myy = M0 * shx + M1;
                updateState();
                return;
            case (APPLY_SHEAR | APPLY_TRANSLATE):
            case (APPLY_SHEAR):
                mxx = mxy * shy;
                myy = myx * shx;
                if (mxx != 0.0 || myy != 0.0) {
                    this.state = mystate | APPLY_SCALE;
                }
                this.type = TYPE_UNKNOWN;
                return;
            case (APPLY_SCALE | APPLY_TRANSLATE):
            case (APPLY_SCALE):
                mxy = mxx * shx;
                myx = myy * shy;
                if (mxy != 0.0 || myx != 0.0) {
                    this.state = mystate | APPLY_SHEAR;
                }
                this.type = TYPE_UNKNOWN;
                return;
            case (APPLY_TRANSLATE):
            case (APPLY_IDENTITY):
                mxy = shx;
                myx = shy;
                if (mxy != 0.0 || myx != 0.0) {
                    this.state = mystate | APPLY_SCALE | APPLY_SHEAR;
                    this.type = TYPE_UNKNOWN;
                }
                return;
        }
    }

    public void concatenate(BaseTransform Tx) {
        switch (Tx.getDegree()) {
            case IDENTITY:
                return;
            case TRANSLATE:
                translate(Tx.getMxt(), Tx.getMyt());
                return;
            case AFFINE:
                break;
            default:
                degreeError(Degree.AFFINE);
                if (!(Tx instanceof AffineBase)) { Tx = new Affine(Tx); }
                break;
        }
        double M0, M1;
        double Txx, Txy, Tyx, Tyy;
        double Txt, Tyt;
        int mystate = state;
        AffineBase at = (AffineBase) Tx;
        int txstate = at.state;
        switch ((txstate << HI_SHIFT) | mystate) {

            /* ---------- Tx == IDENTITY cases ---------- */
            case (HI_IDENTITY | APPLY_IDENTITY):
            case (HI_IDENTITY | APPLY_TRANSLATE):
            case (HI_IDENTITY | APPLY_SCALE):
            case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE):
            case (HI_IDENTITY | APPLY_SHEAR):
            case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE):
            case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE):
            case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
                return;

            /* ---------- this == IDENTITY cases ---------- */
            case (HI_SHEAR | HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):
                mxy = at.mxy;
                myx = at.myx;
            /* NOBREAK */
            case (HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):
                mxx = at.mxx;
                myy = at.myy;
            /* NOBREAK */
            case (HI_TRANSLATE | APPLY_IDENTITY):
                mxt = at.mxt;
                myt = at.myt;
                state = txstate;
                type = at.type;
                return;
            case (HI_SHEAR | HI_SCALE | APPLY_IDENTITY):
                mxy = at.mxy;
                myx = at.myx;
            /* NOBREAK */
            case (HI_SCALE | APPLY_IDENTITY):
                mxx = at.mxx;
                myy = at.myy;
                state = txstate;
                type = at.type;
                return;
            case (HI_SHEAR | HI_TRANSLATE | APPLY_IDENTITY):
                mxt = at.mxt;
                myt = at.myt;
            /* NOBREAK */
            case (HI_SHEAR | APPLY_IDENTITY):
                mxy = at.mxy;
                myx = at.myx;
                mxx = myy = 0.0;
                state = txstate;
                type = at.type;
                return;

            /* ---------- Tx == TRANSLATE cases ---------- */
            case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
            case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE):
            case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE):
            case (HI_TRANSLATE | APPLY_SHEAR):
            case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE):
            case (HI_TRANSLATE | APPLY_SCALE):
            case (HI_TRANSLATE | APPLY_TRANSLATE):
                translate(at.mxt, at.myt);
                return;

            /* ---------- Tx == SCALE cases ---------- */
            case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
            case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE):
            case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE):
            case (HI_SCALE | APPLY_SHEAR):
            case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE):
            case (HI_SCALE | APPLY_SCALE):
            case (HI_SCALE | APPLY_TRANSLATE):
                scale(at.mxx, at.myy);
                return;

            /* ---------- Tx == SHEAR cases ---------- */
            case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
            case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE):
                Txy = at.mxy; Tyx = at.myx;
                M0 = mxx;
                mxx = mxy * Tyx;
                mxy = M0 * Txy;
                M0 = myx;
                myx = myy * Tyx;
                myy = M0 * Txy;
                type = TYPE_UNKNOWN;
                return;
            case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE):
            case (HI_SHEAR | APPLY_SHEAR):
                mxx = mxy * at.myx;
                mxy = 0.0;
                myy = myx * at.mxy;
                myx = 0.0;
                state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);
                type = TYPE_UNKNOWN;
                return;
            case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
            case (HI_SHEAR | APPLY_SCALE):
                mxy = mxx * at.mxy;
                mxx = 0.0;
                myx = myy * at.myx;
                myy = 0.0;
                state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);
                type = TYPE_UNKNOWN;
                return;
            case (HI_SHEAR | APPLY_TRANSLATE):
                mxx = 0.0;
                mxy = at.mxy;
                myx = at.myx;
                myy = 0.0;
                state = APPLY_TRANSLATE | APPLY_SHEAR;
                type = TYPE_UNKNOWN;
                return;
        }
        // If Tx has more than one attribute, pathIterator is not worth optimizing
        // all of those cases...
        Txx = at.mxx; Txy = at.mxy; Txt = at.mxt;
        Tyx = at.myx; Tyy = at.myy; Tyt = at.myt;
        switch (mystate) {
            default:
                stateError();
            /* NOTREACHED */
            case (APPLY_SHEAR | APPLY_SCALE):
                state = mystate | txstate;
            /* NOBREAK */
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
                M0 = mxx;
                M1 = mxy;
                mxx  = Txx * M0 + Tyx * M1;
                mxy  = Txy * M0 + Tyy * M1;
                mxt += Txt * M0 + Tyt * M1;

                M0 = myx;
                M1 = myy;
                myx  = Txx * M0 + Tyx * M1;
                myy  = Txy * M0 + Tyy * M1;
                myt += Txt * M0 + Tyt * M1;
                type = TYPE_UNKNOWN;
                return;

            case (APPLY_SHEAR | APPLY_TRANSLATE):
            case (APPLY_SHEAR):
                M0 = mxy;
                mxx  = Tyx * M0;
                mxy  = Tyy * M0;
                mxt += Tyt * M0;

                M0 = myx;
                myx  = Txx * M0;
                myy  = Txy * M0;
                myt += Txt * M0;
                break;

            case (APPLY_SCALE | APPLY_TRANSLATE):
            case (APPLY_SCALE):
                M0 = mxx;
                mxx  = Txx * M0;
                mxy  = Txy * M0;
                mxt += Txt * M0;

                M0 = myy;
                myx  = Tyx * M0;
                myy  = Tyy * M0;
                myt += Tyt * M0;
                break;

            case (APPLY_TRANSLATE):
                mxx  = Txx;
                mxy  = Txy;
                mxt += Txt;

                myx  = Tyx;
                myy  = Tyy;
                myt += Tyt;
                state = txstate | APPLY_TRANSLATE;
                type = TYPE_UNKNOWN;
                return;
        }
        updateState();
    }

    public void invert() throws NonInvertibleTransformException {
        double Mxx, Mxy, Mxt;
        double Myx, Myy, Myt;
        double det;
        switch (state) {
            default:
                stateError();
            case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
                Mxx = mxx; Mxy = mxy; Mxt = mxt;
                Myx = myx; Myy = myy; Myt = myt;
                det = Mxx * Myy - Mxy * Myx;
                if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NonInvertibleTransformException("Determinant is " + det); }
                mxx =  Myy / det;
                myx = -Myx / det;
                mxy = -Mxy / det;
                myy =  Mxx / det;
                mxt = (Mxy * Myt - Myy * Mxt) / det;
                myt = (Myx * Mxt - Mxx * Myt) / det;
                break;
            case (APPLY_SHEAR | APPLY_SCALE):
                Mxx = mxx; Mxy = mxy;
                Myx = myx; Myy = myy;
                det = Mxx * Myy - Mxy * Myx;
                if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NonInvertibleTransformException("Determinant is " + det); }
                mxx =  Myy / det;
                myx = -Myx / det;
                mxy = -Mxy / det;
                myy =  Mxx / det;
                break;
            case (APPLY_SHEAR | APPLY_TRANSLATE):
                Mxy = mxy; Mxt = mxt;
                Myx = myx; Myt = myt;
                if (Mxy == 0.0 || Myx == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                myx = 1.0 / Mxy;
                mxy = 1.0 / Myx;
                mxt = -Myt / Myx;
                myt = -Mxt / Mxy;
                break;
            case (APPLY_SHEAR):
                Mxy = mxy;
                Myx = myx;
                if (Mxy == 0.0 || Myx == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                myx = 1.0 / Mxy;
                mxy = 1.0 / Myx;
                break;
            case (APPLY_SCALE | APPLY_TRANSLATE):
                Mxx = mxx; Mxt = mxt;
                Myy = myy; Myt = myt;
                if (Mxx == 0.0 || Myy == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                mxx = 1.0 / Mxx;
                myy = 1.0 / Myy;
                mxt = -Mxt / Mxx;
                myt = -Myt / Myy;
                break;
            case (APPLY_SCALE):
                Mxx = mxx;
                Myy = myy;
                if (Mxx == 0.0 || Myy == 0.0) { throw new NonInvertibleTransformException("Determinant is 0"); }
                mxx = 1.0 / Mxx;
                myy = 1.0 / Myy;
                break;
            case (APPLY_TRANSLATE):
                mxt = -mxt;
                myt = -myt;
                break;
            case (APPLY_IDENTITY):
                break;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy