package mikera.transformz;
import mikera.indexz.Index;
import mikera.indexz.Indexz;
import mikera.transformz.impl.AConstantTransform;
import mikera.transformz.impl.CompoundTransform;
import mikera.transformz.impl.SubsetTransform;
import mikera.vectorz.AVector;
import mikera.vectorz.Vector;
/**
* Abstract base class for all vector transformations.
*
* A transformation is defined as any function mapping an N-dimensional vector to an M-dimensional vector.
*
* @author Mike
*/
public abstract class ATransform implements Cloneable, ITransform {
// =====================================
// Abstract interface
/**
* Transforms the source vector, storing the result in the given destination vector
* @param source
* @param dest
*/
public abstract void transform(AVector source, AVector dest);
/**
* Returns the number of dimensions required for input vectors
* @return
*/
public abstract int inputDimensions();
/**
* Returns the number of dimensions required for output vectors
* @return
*/
public abstract int outputDimensions();
// =====================================
// Standard implementations
/**
* Clones the transform, performing a deep copy where needed
*/
public ATransform clone() {
try {
return (ATransform) super.clone();
} catch (CloneNotSupportedException e) {
throw new Error("Clone should be supported!!");
}
}
/**
* Composes this transformation with another transformation, returning
* a new combined transformation
*/
public ATransform compose(ATransform trans) {
// transforming a constant should be a constant result
if (trans instanceof AConstantTransform) {
return Transformz.constantTransform(
trans.inputDimensions(),
this.transform(((AConstantTransform)trans).getConstantValue()));
}
return new CompoundTransform(this,trans);
}
/**
* Composes this transformation with a given transformation,
* mutating the transformation to represent the combined transform
*/
public void composeWith(ATransform trans) {
throw new UnsupportedOperationException(this.getClass()+" cannot compose with "+trans.getClass());
}
/**
* Returns true if this transformation is guaranteed to be linear
* @return
*/
public boolean isLinear() {
return false;
}
/**
* Returns true if the transform is square (same number of input and output dimensions)
* @return
*/
public boolean isSquare() {
return inputDimensions()==outputDimensions();
}
/**
* Transforms a vector, returning a new transformed vector
*
* @param v
* @return
*/
public AVector transform(AVector v) {
Vector temp=Vector.createLength(outputDimensions());
transform(v,temp);
return temp;
}
/**
* Transforms a vector, returning a new transformed vector
*
* @param v
* @return
*/
public Vector transform(Vector v) {
Vector temp=Vector.createLength(outputDimensions());
transform(v,temp);
return temp;
}
/**
* Calculates a single element of the output.
* Not necessarily faster than calculating full output, but can be in some circumstances.
*/
public double calculateElement(int i, AVector inputVector) {
AVector r=transform(inputVector);
return r.unsafeGet(i);
}
/**
* Transforms a vector destructively. Intended for fast non-allocating transforms
* @param v
*/
public void transformInPlace(AVector v) {
throw new UnsupportedOperationException(""+this.getClass()+" does not support transform in place");
}
/**
* Returns true if this transform is known to be the identity function, i.e. it maps all vectors to themselves
*/
public boolean isIdentity() {
throw new UnsupportedOperationException();
}
/**
* Returns a wrapper transform that returns a subset of this transform's output components
*/
public ATransform takeComponents(int length) {
return takeComponents(Indexz.createSequence(length));
}
/**
* Returns a wrapper transform that returns a subset of this transform's output components
*/
public ATransform takeComponents(int start, int length) {
return takeComponents(Indexz.createSequence(start,length));
}
/**
* Returns a wrapper transform that returns a subset of this transform's output components
*/
public ATransform takeComponents(Index components) {
return SubsetTransform.create(this,components);
}
/**
* Return the inverse of this transformation if possible
* @return
*/
public AAffineTransform inverse() {
throw new UnsupportedOperationException("inverse not supported by "+this.getClass());
}
/**
* Returns true if this transform is invertible
*/
public boolean isInvertible() {
return false;
}
}