/**
* License: GPL
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License 2
* as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package mpicbg.trakem2.transform;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Set;
import mpicbg.models.AffineModel2D;
import mpicbg.models.CoordinateTransform;
import mpicbg.models.NoninvertibleModelException;
import mpicbg.models.PointMatch;
/**
*
* @author Stephan Saalfeld saalfeld@mpi-cbg.de
* @version 0.1b
*/
public class TransformMesh extends mpicbg.models.TransformMesh
{
final protected Rectangle boundingBox;
final public Rectangle getBoundingBox(){ return boundingBox; }
public TransformMesh(
final CoordinateTransform t,
final int numX,
final double width,
final double height )
{
super( numX, numY( numX, width, height ), width, height );
double xMin = Double.MAX_VALUE;
double yMin = Double.MAX_VALUE;
double xMax = -Double.MAX_VALUE;
double yMax = -Double.MAX_VALUE;
final Set< PointMatch > vertices = va.keySet();
for ( final PointMatch vertex : vertices )
{
final double[] w = vertex.getP2().getW();
t.applyInPlace( w );
if ( w[ 0 ] < xMin ) xMin = w[ 0 ];
if ( w[ 0 ] > xMax ) xMax = w[ 0 ];
if ( w[ 1 ] < yMin ) yMin = w[ 1 ];
if ( w[ 1 ] > yMax ) yMax = w[ 1 ];
}
for ( final PointMatch vertex : vertices )
{
final int tx = ( int )xMin;
final int ty = ( int )yMin;
final double[] w = vertex.getP2().getW();
w[ 0 ] -= tx;
w[ 1 ] -= ty;
}
updateAffines();
final double fw = xMax - xMin;
final double fh = yMax - yMin;
final int w = ( int )fw;
final int h = ( int )fh;
boundingBox = new Rectangle( ( int )xMin, ( int )yMin, ( w == fw ? w : w + 1 ), ( h == fh ? h : h + 1 ) );
}
/**
* Catch non-invertible locations outside of the meshes boundaries and
* transfer them with the affine defined by the `closest' affine (the affine
* whose summed up control points distances to location are smallest).
*/
@Override
public void applyInverseInPlace( final double[] location ) throws NoninvertibleModelException
{
assert location.length == 2 : "2d transform meshs can be applied to 2d points only.";
final Set< AffineModel2D > s = av.keySet();
for ( final AffineModel2D ai : s )
{
final ArrayList< PointMatch > pm = av.get( ai );
if ( isInConvexTargetPolygon( pm, location ) )
{
ai.applyInverseInPlace( location );
return;
}
}
/* not in the mesh, find the closest affine */
double dMin = Double.MAX_VALUE;
AffineModel2D closestAffine = new AffineModel2D();
final double x = location[ 0 ];
final double y = location[ 1 ];
for ( final AffineModel2D ai : s )
{
final ArrayList< PointMatch > pm = av.get( ai );
double d = 0;
for ( final PointMatch p : pm )
{
final double[] w = p.getP2().getW();
final double dx = w[ 0 ] - x;
final double dy = w[ 1 ] - y;
d += Math.sqrt( dx * dx + dy * dy );
}
if ( d < dMin )
{
dMin = d;
closestAffine = ai;
}
}
closestAffine.applyInverseInPlace( location );
throw new NoninvertibleModelException( "Mesh external location ( " + x + ", " + y + " ) transferred to ( " + location[ 0 ] + ", " + location[ 1 ] + " ) by closest affine." );
}
/**
* Return the {@link AffineModel2D} used by the triangle enclosing or
* closest to the passed target location.
*
* @param location
* @return
*/
public AffineModel2D closestSourceAffine( final double[] location )
{
assert location.length == 2 : "2d transform meshs can be applied to 2d points only.";
final Set< AffineModel2D > s = av.keySet();
for ( final AffineModel2D ai : s )
{
final ArrayList< PointMatch > pm = av.get( ai );
if ( isInSourcePolygon( pm, location ) )
return ai;
}
/* not in the mesh, find the closest affine */
double dMin = Double.MAX_VALUE;
AffineModel2D closestAffine = new AffineModel2D();
final double x = location[ 0 ];
final double y = location[ 1 ];
for ( final AffineModel2D ai : s )
{
final ArrayList< PointMatch > pm = av.get( ai );
double d = 0;
for ( final PointMatch p : pm )
{
final double[] l = p.getP1().getL();
final double dx = l[ 0 ] - x;
final double dy = l[ 1 ] - y;
d += Math.sqrt( dx * dx + dy * dy );
}
if ( d < dMin )
{
dMin = d;
closestAffine = ai;
}
}
return closestAffine;
}
/**
* Return the {@link AffineModel2D} used by the triangle enclosing or
* closest to the passed target location.
*
* @param location
* @return
*/
public AffineModel2D closestTargetAffine( final double[] location )
{
assert location.length == 2 : "2d transform meshs can be applied to 2d points only.";
final Set< AffineModel2D > s = av.keySet();
for ( final AffineModel2D ai : s )
{
final ArrayList< PointMatch > pm = av.get( ai );
if ( isInConvexTargetPolygon( pm, location ) )
return ai;
}
/* not in the mesh, find the closest affine */
double dMin = Double.MAX_VALUE;
AffineModel2D closestAffine = new AffineModel2D();
final double x = location[ 0 ];
final double y = location[ 1 ];
for ( final AffineModel2D ai : s )
{
final ArrayList< PointMatch > pm = av.get( ai );
double d = 0;
for ( final PointMatch p : pm )
{
final double[] w = p.getP2().getW();
final double dx = w[ 0 ] - x;
final double dy = w[ 1 ] - y;
d += Math.sqrt( dx * dx + dy * dy );
}
if ( d < dMin )
{
dMin = d;
closestAffine = ai;
}
}
return closestAffine;
}
}