/* * @(#)AbstractShapeTransition2D.java * * $Date: 2014-03-14 07:36:22 +0100 (P, 14 márc. 2014) $ * * Copyright (c) 2011 by Jeremy Wood. * All rights reserved. * * The copyright of this software is owned by Jeremy Wood. * You may not use, copy or modify this software, except in * accordance with the license agreement you entered into with * Jeremy Wood. For details see accompanying license terms. * * This software is probably, but not necessarily, discussed here: * https://javagraphics.java.net/ * * That site should also contain the most recent official version * of this software. (See the SVN repository for more details.) */ package com.bric.image.transition; import java.awt.Dimension; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.Rectangle2D; import java.util.Hashtable; import com.bric.geom.ShapeBounds; /** This takes any abstract silhouette and will zoom it in/out to reveal * the new frame. * * <P>This class is actually a little bit clever, because it will study * exactly to what proportion it has to zoom the silhouette to completely * cover the frame size. * */ public abstract class AbstractShapeTransition2D extends Transition2D { int type; /** Creates a new AbstractShapeTransition2D that zooms out * */ public AbstractShapeTransition2D() { this(OUT); } /** Creates a new AbstractShapeTransition2D * * @param type must be IN or OUT. * <P>This indicates whether the shape grows or shrinks as this * transition progresses. */ public AbstractShapeTransition2D(int type) { if(!(type==IN || type==OUT)) throw new IllegalArgumentException("Type must be IN or OUT."); this.type = type; } public abstract Shape getShape(); Hashtable<Dimension, Number> multipliers = new Hashtable<Dimension, Number>(); /** Calculating the scaling ratio for the shape to fit the dimensions provided. */ protected float calculateMultiplier(Dimension size) { Shape shape = getShape(); Area base = new Area(shape); AffineTransform transform = new AffineTransform(); Rectangle2D r = ShapeBounds.getBounds(base); transform.translate(size.width/2f-r.getCenterX(),size.height/2f-r.getCenterY()); base.transform(transform); r = ShapeBounds.getBounds(base,r); float min = 0; float max = 1; Rectangle2D boundsRect = new Rectangle2D.Float(0,0,size.width,size.height); while(isOK(base,r,boundsRect,max)==false) { min = max; max*=1.2; } float f = calculateMultiplier(base,r,boundsRect,min,max); isOK(base,r,boundsRect,f); return f; } /** Perform a binary search for the best-fitting multiplier to use */ private float calculateMultiplier(Area shape,Rectangle2D shapeBounds,Rectangle2D bounds,float min,float max) { if(max-min<.5) return max; float middle = (min+max)/2f; if(isOK(shape,shapeBounds,bounds,middle)) { return calculateMultiplier(shape,shapeBounds,bounds,min,middle); } else { return calculateMultiplier(shape,shapeBounds,bounds,middle,max); } } /** Determine if a particular scaling ratio works */ private boolean isOK(Area shape, Rectangle2D shapeBounds,Rectangle2D bounds,float ratio) { Area area = new Area(shape); area.transform(AffineTransform.getScaleInstance(ratio, ratio)); Rectangle2D r = ShapeBounds.getBounds(area); area.transform(AffineTransform.getTranslateInstance(-r.getCenterX()+bounds.getCenterX(), -r.getCenterY()+bounds.getCenterY())); Area boundsArea = new Area(bounds); boundsArea.subtract(area); return boundsArea.isEmpty(); } @Override public Transition2DInstruction[] getInstructions(float progress,Dimension size) { Number multiplier = multipliers.get(size); if(multiplier==null) { multiplier = new Float(calculateMultiplier(size)); multipliers.put( size, multiplier ); } if(type==IN) { progress = 1-progress; } Shape clipping = getShape(); Rectangle2D r = ShapeBounds.getBounds(clipping); AffineTransform transform = new AffineTransform(); transform.setToIdentity(); transform.translate(size.width/2, size.height/2); transform.scale(progress*multiplier.floatValue(),progress*multiplier.floatValue()); transform.translate(-size.width/2, -size.height/2); transform.translate(-r.getCenterX()+size.width/2f,-r.getCenterY()+size.height/2f); clipping = transform.createTransformedShape(clipping); return new Transition2DInstruction[] { new ImageInstruction(type==OUT), new ImageInstruction(type!=OUT, null, clipping) }; } public abstract String getShapeName(); @Override public String toString() { if(type==IN) { return getShapeName()+" In"; } return getShapeName()+" Out"; } }