/*-
* #%L
* Fiji distribution of ImageJ for the life sciences.
* %%
* Copyright (C) 2007 - 2017 Fiji developers.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-2.0.html>.
* #L%
*/
package mpicbg.pointdescriptor;
import java.util.ArrayList;
import spim.vecmath.Matrix3d;
import spim.vecmath.Vector3d;
import mpicbg.models.Point;
import mpicbg.models.PointMatch;
import mpicbg.pointdescriptor.exception.NoSuitablePointsException;
import fiji.util.node.Leaf;
public class LocalCoordinateSystemPointDescriptor < P extends Point > extends AbstractPointDescriptor< P, LocalCoordinateSystemPointDescriptor<P> >
implements Leaf< LocalCoordinateSystemPointDescriptor<P> >
{
final protected boolean normalize;
public float ax = 1, bx, by, cx, cy, cz;
public LocalCoordinateSystemPointDescriptor( final P basisPoint, final ArrayList<P> orderedNearestNeighboringPoints,
final boolean normalize ) throws NoSuitablePointsException
{
super( basisPoint, orderedNearestNeighboringPoints, null, null );
if ( numDimensions != 3 )
throw new NoSuitablePointsException( "LocalCoordinateSystemPointDescriptor does not support dim = " + numDimensions + ", only dim = 3 is valid." );
/* check that number of points is at least model.getMinNumMatches() */
if ( numNeighbors() != 3 )
throw new NoSuitablePointsException( "Only 3 nearest neighbors is supported by a LocalCoordinateSystemPointDescriptor : num neighbors = " + numNeighbors() );
this.normalize = normalize;
buildLocalCoordinateSystem( descriptorPoints, normalize );
}
@Override
public double descriptorDistance( final LocalCoordinateSystemPointDescriptor< P > pointDescriptor )
{
double difference = 0;
if ( !normalize )
difference += ( ax - pointDescriptor.ax ) * ( ax - pointDescriptor.ax );
difference += ( bx - pointDescriptor.bx ) * ( bx - pointDescriptor.bx );
difference += ( by - pointDescriptor.by ) * ( by - pointDescriptor.by );
difference += ( cx - pointDescriptor.cx ) * ( cx - pointDescriptor.cx );
difference += ( cy - pointDescriptor.cy ) * ( cy - pointDescriptor.cy );
difference += ( cz - pointDescriptor.cz ) * ( cz - pointDescriptor.cz );
return difference;// / 3.0;
}
/**
* Not necessary as the main matching method is overwritten
*/
@Override
public Object fitMatches( final ArrayList<PointMatch> matches ) { return null; }
public void buildLocalCoordinateSystem( final ArrayList< LinkedPoint< P > > neighbors, final boolean normalize )
{
// most distant point
final Vector3d b = new Vector3d( neighbors.get( 0 ).getL() );
final Vector3d c = new Vector3d( neighbors.get( 1 ).getL() );
final Vector3d d = new Vector3d( neighbors.get( 2 ).getL() );
final Vector3d x = new Vector3d( d );
x.normalize();
// IOFunctions.println( "Input" );
// IOFunctions.println( b );
// IOFunctions.println( c );
// IOFunctions.println( d );
if ( normalize )
{
final double lengthD = 1.0 / d.length();
b.scale(lengthD);
c.scale(lengthD);
d.scale(lengthD);
// IOFunctions.println( "Scaled" );
// IOFunctions.println( b + "(" + b.length() + ")");
// IOFunctions.println( c + "(" + c.length() + ")");
// IOFunctions.println( d + "(" + d.length() + ")");
}
else
{
ax = (float)d.length();
}
// get normal vector of ab and ad ( which will be the z-axis)
final Vector3d n = new Vector3d();
n.cross(b, x);
n.normalize();
// IOFunctions.println( "Normal vector (z-axis)" );
// IOFunctions.println( n );
// check if the normal vector points into the direction of point c
if ( n.dot( c ) < 0 )
{
n.negate();
// IOFunctions.println( "Negated normal vector (z-axis)" );
// IOFunctions.println( n );
}
// get the inverse of the matrix that maps the vectors into the local coordinate system
// where the x-axis is vector(ad), the z-axis is n and the y-axis is cross-product(x,z)
final Vector3d y = new Vector3d();
y.cross( n, x );
y.normalize();
// IOFunctions.println( "X - axis" );
// IOFunctions.println( x );
//
// IOFunctions.println( "Y - axis" );
// IOFunctions.println( y );
final Matrix3d m = new Matrix3d();
m.m00 = x.x; m.m01 = y.x; m.m02 = n.x;
m.m10 = x.y; m.m11 = y.y; m.m12 = n.y;
m.m20 = x.z; m.m21 = y.z; m.m22 = n.z;
try
{
m.invert();
}
catch ( Exception e )
{
bx = by = cx = cy = cz = 0;
return;
}
// get the positions in the local coordinate system
final Vector3d bl = new Vector3d( b );
final Vector3d cl = new Vector3d( c );
m.transform( bl );
m.transform( cl );
// IOFunctions.println( "In local coordinate system" );
// IOFunctions.println( bl );
// IOFunctions.println( cl );
bx = (float)bl.x;
by = (float)bl.y;
cx = (float)cl.x;
cy = (float)cl.y;
cz = (float)cl.z;
// System.out.println( "NEW" );
// System.out.println( ax );
// System.out.println( bx );
// System.out.println( by );
// System.out.println( cx );
// System.out.println( cy );
// System.out.println( cz );
//
// System.exit( 0 );
}
@SuppressWarnings("unchecked")
@Override
public LocalCoordinateSystemPointDescriptor<P>[] createArray( final int n )
{
return new LocalCoordinateSystemPointDescriptor[ n ];
}
@Override
public float distanceTo( final LocalCoordinateSystemPointDescriptor<P> other )
{
return (float) Math.sqrt( descriptorDistance( other ) );
}
@Override
public float get( final int k )
{
if ( normalize )
{
if ( k == 0 )
return bx;
else if ( k == 1 )
return by;
else if ( k == 2 )
return cx;
else if ( k == 3 )
return cy;
else
return cz;
}
else
{
if ( k == 0 )
return ax;
else if ( k == 1 )
return bx;
else if ( k == 2 )
return by;
else if ( k == 3 )
return cx;
else if ( k == 4 )
return cy;
else
return cz;
}
}
@Override
public int getNumDimensions()
{
if ( normalize )
return 5;
else
return 6;
}
@Override
public boolean isLeaf() { return true; }
@Override
public boolean resetWorldCoordinatesAfterMatching() { return true; }
@Override
public boolean useWorldCoordinatesForDescriptorBuildUp() { return false; }
}