/**
  * Used with serialization. Not to be called manually.
  *
  * @param in ObjectInput
  * @throws IOException
  * @throws ClassNotFoundException
  */
 @Override
 public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
   _myMatrix.set((CCMatrix3x3) in.readObject());
   _myScale.set((CCVector3) in.readObject());
   _myTranslation.set((CCVector3) in.readObject());
   _myIsIdentity = in.readBoolean();
   _myIsRotationMatrix = in.readBoolean();
   _myIsUniformScale = in.readBoolean();
 }
  /**
   * Constructs a new Transform object from the information stored in the given source Transform.
   *
   * @param theSource
   * @throws NullPointerException if source is null.
   */
  public CCTransform(final CCTransform theSource) {
    _myMatrix.set(theSource.getMatrix());
    _myScale.set(theSource.scale());
    _myTranslation.set(theSource.translation());

    _myIsIdentity = theSource.isIdentity();
    _myIsRotationMatrix = theSource.isRotationMatrix();
    _myIsUniformScale = theSource.isUniformScale();
  }
  /**
   * Calculates the product of this transform with the given "transformBy" transform (P = this * T)
   * and stores this in store.
   *
   * @param transformBy
   * @return the product
   * @throws NullPointerException if transformBy is null.
   */
  public CCTransform multiply(final CCTransform transformBy, CCTransform theStore) {
    if (theStore == null) theStore = new CCTransform();

    if (_myIsIdentity) {
      return theStore.set(transformBy);
    }

    if (transformBy.isIdentity()) {
      return theStore.set(this);
    }

    if (_myIsRotationMatrix && transformBy.isRotationMatrix() && _myIsUniformScale) {
      theStore._myIsRotationMatrix = true;
      theStore._myMatrix.set(_myMatrix).multiplyLocal(transformBy.getMatrix());

      theStore._myTranslation.set(transformBy.translation());
      theStore._myTranslation.set(_myMatrix.applyPost(theStore._myTranslation));
      // uniform scale, so just use X.
      theStore._myTranslation.multiplyLocal(_myScale.x);
      theStore._myTranslation.addLocal(_myTranslation);

      if (transformBy.isUniformScale()) {
        theStore.scale(_myScale.x * transformBy.scale().x);
      } else {
        final CCVector3 scale = theStore._myScale.set(transformBy.scale());
        scale.multiplyLocal(_myScale.x);
      }

      // update our flags in one place.
      theStore.updateFlags(true);

      return theStore;
    }

    // In all remaining cases, the matrix cannot be written as R*S*X+T.
    final CCMatrix3x3 matrixA =
        isRotationMatrix() ? _myMatrix.multiplyDiagonalPost(_myScale) : _myMatrix;

    final CCMatrix3x3 matrixB =
        transformBy.isRotationMatrix()
            ? transformBy.getMatrix().multiplyDiagonalPost(transformBy.scale())
            : transformBy.getMatrix();

    final CCMatrix3x3 newMatrix = theStore._myMatrix;
    newMatrix.set(matrixA).multiplyLocal(matrixB);

    theStore._myTranslation.set(
        matrixA.applyPost(transformBy.translation()).addLocal(translation()));

    // prevent scale bleeding since we don't set it.
    theStore._myScale.set(1.0f, 1.0f, 1.0f);

    // update our flags in one place.
    theStore.updateFlags(false);

    return theStore;
  }
 /**
  * Resets this transform to identity and resets all flags.
  *
  * @return this Transform for chaining.
  */
 public CCTransform setIdentity() {
   _myMatrix.set(CCMatrix3x3.IDENTITY);
   _myScale.set(CCVector3.ONE);
   _myTranslation.set(CCVector3.ZERO);
   _myIsIdentity = true;
   _myIsRotationMatrix = true;
   _myIsUniformScale = true;
   return this;
 }
  /**
   * Copies the given transform values into this transform object.
   *
   * @param source
   * @return this transform for chaining.
   * @throws NullPointerException if source is null.
   */
  public CCTransform set(final CCTransform source) {
    if (source.isIdentity()) {
      setIdentity();
    } else {
      _myMatrix.set(source.getMatrix());
      _myScale.set(source.scale());
      _myTranslation.set(source.translation());

      _myIsIdentity = false;
      _myIsRotationMatrix = source.isRotationMatrix();
      _myIsUniformScale = source.isUniformScale();
    }
    return this;
  }
  /**
   * Internal only constructor, generally used for making an immutable transform.
   *
   * @param theMatrix
   * @param theScale
   * @param theTranslation
   * @param theIsIdentity
   * @param theIsRotationMatrix
   * @param theIsUniformScale
   * @throws NullPointerException if a param is null.
   */
  protected CCTransform(
      final CCMatrix3x3 theMatrix,
      final CCVector3 theScale,
      final CCVector3 theTranslation,
      final boolean theIsIdentity,
      final boolean theIsRotationMatrix,
      final boolean theIsUniformScale) {
    _myMatrix.set(theMatrix);
    _myScale.set(theScale);
    _myTranslation.set(theTranslation);

    _myIsIdentity = theIsIdentity;
    _myIsRotationMatrix = theIsRotationMatrix;
    _myIsUniformScale = theIsUniformScale;
  }
  /**
   * Reads in a 4x4 matrix as a 3x3 matrix and translation.
   *
   * @param matrix
   * @return this matrix for chaining.
   * @throws NullPointerException if matrix is null.
   */
  public CCTransform fromHomogeneousMatrix(final CCMatrix4x4 matrix) {
    _myMatrix.set(
        matrix.m00,
        matrix.m10,
        matrix.m20,
        matrix.m01,
        matrix.m11,
        matrix.m21,
        matrix.m02,
        matrix.m12,
        matrix.m22);
    _myTranslation.set(matrix.m03, matrix.m13, matrix.m23);

    updateFlags(false);
    return this;
  }
 /**
  * Sets the matrix portion of this transform to the rotational value of the given Quaternion.
  * Calling this allows scale to be set and used.
  *
  * @param theRotation
  * @return this transform for chaining.
  * @throws NullPointerException if rotation is null.
  */
 public CCTransform rotation(final CCQuaternion theRotation) {
   _myMatrix.set(theRotation);
   updateFlags(true);
   return this;
 }
 /**
  * Sets the matrix portion of this transform to the given value.
  *
  * <p>NB: Calling this with a matrix that is not purely rotational (orthonormal) will result in a
  * Transform whose scale comes from its matrix. Further attempts to set scale directly will throw
  * an error.
  *
  * @param theRotation our new matrix.
  * @return this transform for chaining.
  * @throws NullPointerException if rotation is null.
  * @see CCMatrix3x3#isOrthonormal()
  */
 public CCTransform rotation(final CCMatrix3x3 theRotation) {
   _myMatrix.set(theRotation);
   updateFlags(false);
   return this;
 }