package ch.sahits.game.graphic.image.impl;
import java.awt.Point;
import org.apache.log4j.Logger;
import ch.sahits.game.graphic.image.IPolygonScaling;
import ch.sahits.game.graphic.image.model.ImageData;
import ch.sahits.game.graphic.image.model.NamedPolygon;
import ch.sahits.game.image.ECrop;
import ch.sahits.game.image.EScaleDirection;
import ch.sahits.game.image.ImageScaleState;
/**
* Implementation class for recalculating poligons of a scaled, bordered and cropped image and
* on the absolute positioning of that image on the screen.
* @author Andi Hotz, (c) Sahits GmbH, 2012
* Created on Jul 1, 2012
*
*/
public class PolygonScaling implements IPolygonScaling {
private final Logger logger = Logger.getLogger(PolygonScaling.class);
@Override
public ImageData recalculatePolygons(ImageData imgData,ImageScaleState state) {
ImageData result=null;
// order is important: we do not want to scale translocated polygons
if (state.isScaled()){
result = recalculatePolygonScale(imgData, state);
}
if (state.isBordered() && result==null){
result = recalculatePolygonBorder(imgData, state);
} else if (state.isBordered()){
result = recalculatePolygonBorder(result, state);
}
if (state.isCropped() && result==null){
result = recalculatePolygonCrop(imgData, state);
} else if (state.isCropped() ){ // and bordered
result = recalculatePolygonCrop(result, state);
}
if (result==null){ // no scaling operation at all
result = createNewCopy(imgData);
}
return result;
}
@Override
public ImageData recalculatePolygonsOffsett(ImageData imgData, Point offset) {
if (offset.x==0 && offset.y==0){
return createNewCopy(imgData);
}
ImageData result = createNewCopy(imgData);
for (NamedPolygon poly : result.getPolygons().values()) {
updateCoordinates(poly, offset.x, offset.y);
}
return result;
}
/**
* Compute the polygon affected by the bordering of the image as stated in state
* @param imgData Input data of the image containing a list of {@link NamedPolygon}s that are to be recalculated
* @param state of the scaled image
* @return new instance of ImageData containing the recalculated polygons
*/
final ImageData recalculatePolygonBorder(ImageData imgData, ImageScaleState state){
if (!state.isBordered()){
logger.trace("Image has no border, no recalculation for borders");
return createNewCopy(imgData);
}
if (state.getBordered()==0 || state.getBorderDirection()==null){
throw new IllegalStateException("Border amount and direction must be specified");
}
final int amount = state.getBordered();
ImageData result = createNewCopy(imgData);
if (state.getBorderDirection()==EScaleDirection.HORIZONTAL){ // change x coordinate
logger.trace("Borders on the side: update X coordinate by "+amount);
for (NamedPolygon poly : result.getPolygons().values()) {
updateXCoordinate(poly, amount);
}
} else { // change y coordinate
logger.trace("Borders at the top and bottom: update Y coordinate by "+amount);
for (NamedPolygon poly : result.getPolygons().values()) {
updateYCoordinate(poly, amount);
}
}
return result;
}
/**
* Compute the polygon affected by the cropping of the image as stated in state
* @param imgData Input data of the image containing a list of {@link NamedPolygon}s that are to be recalculated
* @param state of the scaled image
* @return new instance of ImageData containing the recalculated polygons
*/
final ImageData recalculatePolygonCrop(ImageData imgData, ImageScaleState state){
if (!state.isCropped() || state.getCrop()==ECrop.NONE){
// not cropped at all => return new instance of imgData
logger.trace("Image has no cropping, no recalculation for crop");
return createNewCopy(imgData);
}
if (state.getCropped()==0 || state.getCrop()==ECrop.NONE){
throw new IllegalStateException("Crop amount and crop side must be specified");
}
ImageData result = createNewCopy(imgData);
final int amount = state.getCropped();
if (state.getCrop()==ECrop.BOTTOM || state.getCrop()==ECrop.RIGHT){
logger.trace("Image has was cropped at the bottom or the right side: no change");
return result;
} else if (state.getCrop()==ECrop.LEFT){ // change x coordinate
logger.trace("Crop on the left side: update X coordinate by "+amount);
for (NamedPolygon poly : result.getPolygons().values()) {
updateXCoordinate(poly, (int)Math.rint(-amount*state.getScaleFactor()));
}
} else { // change y coordinate
logger.trace("Crop at the top: update Y coordinate by "+amount);
for (NamedPolygon poly : result.getPolygons().values()) {
updateYCoordinate(poly, (int)Math.rint(-amount*state.getScaleFactor()));
}
}
return result;
}
/**
* Create a clone of the image data and handle the CloneNotSupportedException
* @param imgData
* @return
*/
private ImageData createNewCopy(ImageData imgData) {
try {
return (ImageData) imgData.clone();
} catch (CloneNotSupportedException e){
System.err.println("Implementaion error");
e.printStackTrace();
return null;
}
}
/**
* Compute the polygon affected by the scaling of the image as stated in state
* @param imgData Input data of the image containing a list of {@link NamedPolygon}s that are to be recalculated
* @param state of the scaled image
* @return new instance of ImageData containing the recalculated polygons
*/
final ImageData recalculatePolygonScale(ImageData imgData, ImageScaleState state){
if (!state.isScaled()){
// not scaled at all => return new instance of imgData
logger.trace("Image was not scaled, no recalculation for scale");
return createNewCopy(imgData);
}
if (state.getScaleFactor()==1 ){
throw new IllegalStateException("Scale factor must be specified");
}
ImageData result = createNewCopy(imgData);
final double scaleFactor = state.getScaleFactor();
logger.trace("Scale by factor "+scaleFactor);
for (NamedPolygon poly : result.getPolygons().values()) {
updateCoordinate(poly, scaleFactor);
}
return result;
}
/**
* Update the coordinates of the polygon by the specified values in x and y direction
* @param poly polygon to be updated
* @param diffX difference in x direction
* @param diffY difference in y direction
*/
private void updateCoordinates(NamedPolygon poly, int diffX, int diffY){
for (int i = 0; i < poly.npoints; i++) {
logger.trace("Move point by: ("+diffX+","+diffY+") ("+poly.xpoints[i]+","+poly.ypoints[i]+") => ("+(poly.xpoints[i]+diffX)+","+(poly.ypoints[i]+diffY)+")");
poly.xpoints[i] += diffX;
poly.ypoints[i] += diffY;
}
}
/**
* Update the x coordinate of each point within the polygon by amount
* @param poly polygon to be updated
* @param amount the X-coordinate is to be translated
*/
private void updateXCoordinate(NamedPolygon poly,int amount){
for (int i = 0; i < poly.npoints; i++) {
logger.trace("Move point by: "+amount+" ("+poly.xpoints[i]+","+poly.ypoints[i]+") => ("+(poly.xpoints[i]+amount)+","+(poly.ypoints[i])+")");
poly.xpoints[i] += amount;
}
}
/**
* Update the y coordinate of each point within the polygon by amount
* @param poly polygon to be updated
* @param amount the Y-coordinate is to be translated
*/
private void updateYCoordinate(NamedPolygon poly,int amount){
for (int i = 0; i < poly.npoints; i++) {
logger.trace("Move point by: "+amount+" ("+poly.xpoints[i]+","+poly.ypoints[i]+") => ("+(poly.xpoints[i])+","+(poly.ypoints[i]+amount)+")");
poly.ypoints[i] += amount;
}
}
/**
* Update the x and y coordinate of each point within the polygon by factor
* @param poly polygon to be updated
* @param factor that is to be multiplied with each coordinate
*/
private void updateCoordinate(NamedPolygon poly,double factor){
for (int i = 0; i < poly.npoints; i++) {
final int newX = (int)Math.rint(poly.xpoints[i]*factor);
final int newY = (int)Math.rint(poly.ypoints[i]*factor);
logger.trace("Update point factor: "+factor+" ("+poly.xpoints[i]+","+poly.ypoints[i]+") => ("+newX+","+newY+")");
poly.xpoints[i] = newX;
poly.ypoints[i] = newY;
}
}
}