package examples;

import java.util.Vector;
import java.awt.Color;
import java.util.Iterator;
import java.util.TreeMap;

import sema.Element;
import sema.Map;
import sema.Drawing;
import sema.Area;
import sema.Box;

/**
 * Zone rectangulaire orientée.<br>
 * La méthode getIntersectedBoxes a été implantée de façon exacte (pas de cases en trop renvoyées) mais elle ne
 * possède pas de complexité satisfaisante pour des simulations très gourmandes en temps.<br>
 * Pour optimiser le temps, il faut utiliser une version affaiblie d'AreaRectangular qui calcule plus grossièrement la couverture en terme de cases.
 */
public class RectangularArea2 implements Area, Cloneable {
    
    /**
     * Instancie un rectangle à partir de son point de référence et de ses dimensions.
     * L'inclinaison est initialisé à 0, et le point de référence se situe au centre de gravité du rectangle.
     */
    public RectangularArea2(Map map, float x, float y, float x_length, float y_length) {
        this(map, x, y, x_length, y_length, 0, -x_length/2, -y_length/2);
    }
    
    /**
     * Instancie un rectangle à partir de son point de référence et de ses dimensions.
     * L'inclinaison initiale vaut 'angle', et le point de référence se situe au centre de gravité du rectangle.
     */
    public RectangularArea2(Map map, float x, float y, float x_length, float y_length, float angle) {
        this(map, x, y, x_length, y_length, angle, -x_length/2, -y_length/2);
    }
    
    /**
     * Instancie un rectangle à partir de son point de référence et de ses dimensions.
     * L'inclinaison initiale vaut 'angle', et le coin supérieur gauche du rectangle est obtenu
     * par une translation de vecteur (x0_t,y0_t) lorsque l'inclinaison est nulle.
     */
    public RectangularArea2(Map map, float x, float y, float x_length, float y_length, float angle, float x0_t, float y0_t) {
        this.x = x;
        this.y = y;
        this.x0_t = x0_t;
        this.y0_t = y0_t;
        this.x_length = x_length;
        this.y_length = y_length;
        this.angle = angle;
        this.map = map;
        this.boxLength = map.boxLength;
        this.xBoxes = map.xBoxes;
        this.yBoxes = map.yBoxes;
        this.boxes = map.boxes;
        this.xmax = map.xLength;
        this.ymax = map.yLength;
        this.lastX = x;
        this.lastY = y;
        this.lastX0t = x0_t;
        this.lastY0t = y0_t;
        this.lastAngle = angle;
        this.lastXlength = x_length;
        this.lastYlength = y_length;
        refreshValues();
        refreshIntersectedBoxes();
        this.lastV = getIntersectedBoxes();
    }
    
    
    private void refreshValues(){ //met à jour certaines valeurs internes lorsque la zone se déplace
        this.cos = (float)Math.cos((double)angle);
        this.sin = (float)Math.sin((double)angle);
        this.x0 = x0_t+x; //on ne prend pas en compte l'angle pour le point (x0,y0), c'est utile pour le dessin
        this.y0 = y0_t+y;
    }
    
    private Map map; //carte relative à la zone
    private Element element; //élément du monde dont l'instance présente est la zone
    
        /* Définit l'élément dont la zone implémente la forme.
         */
    public void setElement(Element el) {
        this.element = el;
        refreshIntersectedBoxes();
    }
    
    
    //(x,y) est le point de référence de la zone (les coordonnées d'un élément du monde correspondent à ce point)
    private float x;
    private float y;
    private float lastX;
    private float lastY;
    private float lastAngle;
    private float lastXlength;
    private float lastYlength;
    private float lastX0t;
    private float lastY0t;
    private Vector lastV;
    
    //angle: il faut faire une rotation de centre (x,y) et de cet angle pour obtenir la zone
    private float angle;
    
    //dimensions du rectangle
    private float x_length;
    private float y_length;
    
    //point supérieur gauche du rectangle obtenu à partir du point (x,y) et du vecteur 0_t pour angle=0
    private float x0_t;
    private float y0_t;
    
    //point supérieur gauche du rectangle, calculé sans tenir compte de l'angle
    private float x0;
    private float y0;
    
    //valeurs utiles précalculées
    private float cos;
    private float sin;
    
    //constantes de la carte
    private final float boxLength;
    private final int xBoxes;
    private final int yBoxes;
    private final Box[][] boxes;
    private final float xmax;
    private final float ymax;
    
    
    public float getX() {
        return(x);
    }
    
    public float getY() {
        return(y);
    }
    
    public float getAngle() {
        return(angle);
    }
    
    
    //cases occupées par le rectangle
    private Vector intersectedBoxes = new Vector();
    
    private void registerBoxes() { //enregistre l'élément auprès des cases occupées par la zone
        if (element!=null)
            for (int i=0; i<intersectedBoxes.size(); i++) {
                {
                    ((Box) intersectedBoxes.get(i)).register(element);
                }
            }
    }
    
    private void unregisterBoxes() { //désenregistre l'élément auprès des cases occupées par la zone
        if (element!=null)
            for (int i=0; i<intersectedBoxes.size(); i++) {
                {
                    ((Box) intersectedBoxes.get(i)).unregister(element);
                }
            }
        
    }
    
    private Vector getCoveringBoxes() { //renvoie un vecteur contenant les cases intersectées par la zone
        TreeMap covering = new TreeMap();
        float x1 = cos*(x0-x)-sin*(y0-y)+x;
        float y1 = cos*(y0-y)+sin*(x0-x)+y;
        float x2 = cos*(x0+x_length-x)-sin*(y0-y)+x;
        float y2 = cos*(y0-y)+sin*(x0+x_length-x)+y;
        float x3 = cos*(x0-x)-sin*(y0+y_length-y)+x;
        float y3 = cos*(y0+y_length-y)+sin*(x0-x)+y;
        float x4 = cos*(x0+x_length-x)-sin*(y0+y_length-y)+x;
        float y4 = cos*(y0+y_length-y)+sin*(x0+x_length-x)+y;
        float ymin = 0;
        float ymax = 0;
        if (angle>=0 & angle<=Math.PI/2) {
            float xtemp=x1, ytemp=y1;
            x1=x3; y1=y3;
            x3=x4; y3=y4;
            x4=x2; y4=y2;
            x2=xtemp; y2=ytemp;
        } else
            if (angle>Math.PI/2 & angle<=Math.PI) {
            float xtemp=x1, ytemp=y1;
            x1=x4; y1=y4;
            x4=xtemp; y4=ytemp;
            xtemp = x2; ytemp=y2;
            x2=x3; y2=y3;
            x3=xtemp; y3=ytemp;
            } else if (angle>Math.PI & angle<=3*Math.PI/2) {
            float xtemp=x1, ytemp=y1;
            x1=x2; y1=y2;
            x2=x4; y2=y4;
            x4=x3; y4=y3;
            x3=xtemp; y3=ytemp;
            }
        
        int imin = (int)Math.max((Math.ceil(x1/boxLength)),0);
        double imax = Math.min(Math.floor((x4)/boxLength),xBoxes-1);
        for (int i= imin; i<=imax; i++) {
            if (i*boxLength<=x2) {
                ymin = y1+(y2-y1)*(i*boxLength-x1)/(x2-x1);
            } else {
                ymin = y2+(y4-y2)*(i*boxLength-x2)/(x4-x2);
            }
            if (i*boxLength<=x3) {
                ymax = y1+(y3-y1)*(i*boxLength-x1)/(x3-x1);
            } else {
                ymax = y3+(y4-y3)*(i*boxLength-x3)/(x4-x3);
            }
            int jmin = (int)Math.max((Math.floor(ymin/boxLength)),0);
            double jmax = Math.min(Math.floor((ymax)/boxLength), yBoxes-1);
            for (int j= jmin; j<=jmax; j++) {
                try {
                    covering.put(new Integer(i*yBoxes+j),boxes[i][j]);
                } catch (Exception e) {}
            }
        }
        
        int jmin = (int)Math.max((Math.ceil(y2/boxLength)),1);
        double jmax = Math.min(Math.floor((y3)/boxLength),yBoxes);
        for (int j= jmin; j<=jmax; j++) {
            float xmin = 0;
            if (j*boxLength<=y1) {
                xmin = x1+(j*boxLength-y1)/(y2-y1)*(x2-x1);
                int i = (int)Math.floor(xmin/boxLength);
                try {
                    covering.put(new Integer(i*yBoxes+j-1),boxes[i][j-1]);
                } catch (Exception e) {}
                
            } else {
                xmin = x1+(j*boxLength-y1)/(y3-y1)*(x3-x1);
                int i = (int)Math.floor(xmin/boxLength);
                if (j<yBoxes)
                    try {
                        covering.put(new Integer(i*yBoxes+j),boxes[i][j]);
                    } catch (Exception e) {}
            }
        }
        
        try {
            covering.put(new Integer((int)(Math.floor(x1/boxLength))*yBoxes+(int)(y1/boxLength)),boxes[(int)(x1/boxLength)][(int)(y1/boxLength)]);
        } catch (Exception e) {}
        try {
            covering.put(new Integer((int)(Math.floor(x2/boxLength))*yBoxes+(int)(y2/boxLength)),boxes[(int)(x2/boxLength)][(int)(y2/boxLength)]);
        } catch (Exception e) {}
        try {
            covering.put(new Integer((int)(Math.floor(x3/boxLength))*yBoxes+(int)(y3/boxLength)),boxes[(int)(x3/boxLength)][(int)(y3/boxLength)]);
        } catch (Exception e) {}
        try {
            covering.put(new Integer((int)(Math.floor(x4/boxLength))*yBoxes+(int)(y4/boxLength)),boxes[(int)(x4/boxLength)][(int)(y4/boxLength)]);
        } catch (Exception e) {}
        
        Vector v = new Vector();
        Object key = null;
        Object o = null;
        try {key = covering.firstKey();} catch (Exception e) {key =null;}
        while (key!=null) {
            o = covering.remove(key);
            if (o!=null)
                v.add(o);
            try {key = covering.firstKey();} catch (Exception e) {key =null;}
        }
        return v;
    }
    
    
    private Vector computeIntersectedBoxes(){ //dans le cas où l'angle est multiple de Pi/2, le rectangle est droit et cela simplifie les calculs
        if (this.angle == 0)
            return map.getCoveringBoxes(x0,y0,x_length,y_length);
        else
            if (this.angle == 3*Math.PI/2)
                return map.getCoveringBoxes(x+y0-y,y-x_length-x0+x,y_length,x_length);
            else
                if (this.angle == Math.PI)
                    return map.getCoveringBoxes(x+(x-x0)-x_length,y+(y-y0)-y_length,x_length,y_length);
                else
                    if (this.angle == Math.PI/2)
                        return map.getCoveringBoxes(x-y0+y-y_length,y+x0-x,y_length,x_length);
                    else
                        return getCoveringBoxes();
    }
    
    private void refreshIntersectedBoxes() { //calcule les cases que le rectangle intersecte et les stocke
        unregisterBoxes();
        intersectedBoxes = computeIntersectedBoxes();
        registerBoxes();
    }
    
    public Vector getIntersectedBoxes() {
        return intersectedBoxes;
    }
    
    
    private float vectorial(float px, float py, float ax, float ay, float bx, float by) { //composante suivant z du produit vectoriel PA^PB)
        return((ax-px)*(by-py)-(ay-py)*(bx-px));
    }
    
    public boolean isIn(float px, float py){
        float x1 = cos*(x0-x)-sin*(y0-y)+x;
        float y1 = cos*(y0-y)+sin*(x0-x)+y;
        float x2 = cos*(x0+x_length-x)-sin*(y0-y)+x;
        float y2 = cos*(y0-y)+sin*(x0+x_length-x)+y;
        float x3 = cos*(x0-x)-sin*(y0+y_length-y)+x;
        float y3 = cos*(y0+y_length-y)+sin*(x0-x)+y;
        float x4 = cos*(x0+x_length-x)-sin*(y0+y_length-y)+x;
        float y4 = cos*(y0+y_length-y)+sin*(x0+x_length-x)+y;
        boolean result = vectorial(px,py,x1,y1,x2,y2)*vectorial(px,py,x3,y3,x4,y4) <=0 & vectorial(px,py,x1,y1,x3,y3)*vectorial(px,py,x2,y2,x4,y4) <=0;
        return(result);
    }
    
    public boolean isCrashingElements() { //peut servir de fonction de gestion des collisions si l'on autorise au plus un élément par case
        return isCrashingElements(computeIntersectedBoxes());
    }
    
    public boolean isCrashingElements(Vector v) { //peut servir de fonction de gestion des collisions si l'on autorise au plus un élément par case
        if (v==null) return true;
        for (int i = 0; i<v.size(); i++){
            Box b = (Box) v.get(i);
            if (!b.isFreeFor(element)) return true;
            int s =b.getContents().size();
            if (s>1 || (s==1 & !(b.getContents().contains(element)))) {
                return(true);
            }
        }
        return(false);
    }
    
    
    public Vector getPolygon(){
        Vector v = new Vector();
        float x1 = cos*(x0-x)-sin*(y0-y)+x;
        float y1 = cos*(y0-y)+sin*(x0-x)+y;
        float x2 = cos*(x0+x_length-x)-sin*(y0-y)+x;
        float y2 = cos*(y0-y)+sin*(x0+x_length-x)+y;
        float x3 = cos*(x0-x)-sin*(y0+y_length-y)+x;
        float y3 = cos*(y0+y_length-y)+sin*(x0-x)+y;
        float x4 = cos*(x0+x_length-x)-sin*(y0+y_length-y)+x;
        float y4 = cos*(y0+y_length-y)+sin*(x0+x_length-x)+y;
        float[] p1 = new float[2];
        p1[0]=x1; p1[1]=y1;
        float[] p2 = new float[2];
        p2[0]=x2; p2[1]=y2;
        float[] p3 = new float[2];
        p3[0]=x3; p3[1]=y3;
        float[] p4 = new float[2];
        p4[0]=x4; p4[1]=y4;
        v.add(p1);
        v.add(p2);
        v.add(p4);
        v.add(p3);
        return v;
    }
    
    public boolean isPolygon(){
        return true;
    }
    
    public boolean intersect(float ax, float ay, float bx, float by){
        float x1 = cos*(x0-x)-sin*(y0-y)+x;
        float y1 = cos*(y0-y)+sin*(x0-x)+y;
        float x2 = cos*(x0+x_length-x)-sin*(y0-y)+x;
        float y2 = cos*(y0-y)+sin*(x0+x_length-x)+y;
        float x3 = cos*(x0-x)-sin*(y0+y_length-y)+x;
        float y3 = cos*(y0+y_length-y)+sin*(x0-x)+y;
        float x4 = cos*(x0+x_length-x)-sin*(y0+y_length-y)+x;
        float y4 = cos*(y0+y_length-y)+sin*(x0+x_length-x)+y;
        return (
                vectorial(ax,ay,x1,y1,x2,y2)*vectorial(bx,by,x1,y1,x2,y2)<=0 && vectorial(x1,y1,ax,ay,bx,by)*vectorial(x2,y2,ax,ay,bx,by)<=0 ||
                vectorial(ax,ay,x2,y2,x3,y3)*vectorial(bx,by,x2,y2,x3,y3)<=0 && vectorial(x2,y2,ax,ay,bx,by)*vectorial(x3,y3,ax,ay,bx,by)<=0 ||
                vectorial(ax,ay,x3,y3,x4,y4)*vectorial(bx,by,x3,y3,x4,y4)<=0 && vectorial(x3,y3,ax,ay,bx,by)*vectorial(x4,y4,ax,ay,bx,by)<=0 ||
                vectorial(ax,ay,x4,y4,x1,y1)*vectorial(bx,by,x4,y4,x1,y1)<=0 && vectorial(x4,y4,ax,ay,bx,by)*vectorial(x1,y1,ax,ay,bx,by)<=0
                );
    }
    
    
    
    public boolean intersect(Vector p1){
        float x1 = cos*(x0-x)-sin*(y0-y)+x;
        float y1 = cos*(y0-y)+sin*(x0-x)+y;
        float x2 = cos*(x0+x_length-x)-sin*(y0-y)+x;
        float y2 = cos*(y0-y)+sin*(x0+x_length-x)+y;
        float x3 = cos*(x0-x)-sin*(y0+y_length-y)+x;
        float y3 = cos*(y0+y_length-y)+sin*(x0-x)+y;
        float x4 = cos*(x0+x_length-x)-sin*(y0+y_length-y)+x;
        float y4 = cos*(y0+y_length-y)+sin*(x0+x_length-x)+y;
        int n = p1.size();
        for (int i = 0; i<n-1;i++){
            float[] temp = (float[])p1.get(i);
            float ax = temp[0];
            float ay = temp[1];
            float[] temp2 = (float[])p1.get(i+1);
            float bx = temp2[0];
            float by = temp2[1];
            if (
                    vectorial(ax,ay,x1,y1,x2,y2)*vectorial(bx,by,x1,y1,x2,y2)<=0 && vectorial(x1,y1,ax,ay,bx,by)*vectorial(x2,y2,ax,ay,bx,by)<=0 ||
                    vectorial(ax,ay,x2,y2,x3,y3)*vectorial(bx,by,x2,y2,x3,y3)<=0 && vectorial(x2,y2,ax,ay,bx,by)*vectorial(x3,y3,ax,ay,bx,by)<=0 ||
                    vectorial(ax,ay,x3,y3,x4,y4)*vectorial(bx,by,x3,y3,x4,y4)<=0 && vectorial(x3,y3,ax,ay,bx,by)*vectorial(x4,y4,ax,ay,bx,by)<=0 ||
                    vectorial(ax,ay,x4,y4,x1,y1)*vectorial(bx,by,x4,y4,x1,y1)<=0 && vectorial(x4,y4,ax,ay,bx,by)*vectorial(x1,y1,ax,ay,bx,by)<=0
                    ) return true;
        }
        if (n>1) {
            float[] temp = (float[])p1.get(n-1);
            float ax = temp[0];
            float ay = temp[1];
            float[] temp2 = (float[])p1.get(0);
            float bx = temp2[0];
            float by = temp2[1];
            return (
                    vectorial(ax,ay,x1,y1,x2,y2)*vectorial(bx,by,x1,y1,x2,y2)<=0 && vectorial(x1,y1,ax,ay,bx,by)*vectorial(x2,y2,ax,ay,bx,by)<=0 ||
                    vectorial(ax,ay,x2,y2,x3,y3)*vectorial(bx,by,x2,y2,x3,y3)<=0 && vectorial(x2,y2,ax,ay,bx,by)*vectorial(x3,y3,ax,ay,bx,by)<=0 ||
                    vectorial(ax,ay,x3,y3,x4,y4)*vectorial(bx,by,x3,y3,x4,y4)<=0 && vectorial(x3,y3,ax,ay,bx,by)*vectorial(x4,y4,ax,ay,bx,by)<=0 ||
                    vectorial(ax,ay,x4,y4,x1,y1)*vectorial(bx,by,x4,y4,x1,y1)<=0 && vectorial(x4,y4,ax,ay,bx,by)*vectorial(x1,y1,ax,ay,bx,by)<=0
                    );
        }
        return false;
    }
    
    
    public boolean intersect(Area a){
        return a.intersect(this.getPolygon());
    }
    
    
    
    public boolean exactIsCrashingElements() { //peut servir de fonction de gestion des collisions entre éléments
        return exactIsCrashingElements(computeIntersectedBoxes());
    }
    
    public boolean exactIsCrashingElements(Vector v) { //peut servir de fonction de gestion des collisions entre éléments
        if (v==null) return true;
        for (int i = 0; i<v.size(); i++){
            Box b = (Box) v.get(i);
            if (!b.isFreeFor(element)) return true;
            Iterator it = b.getContents().iterator();
            while (it.hasNext()) {
                Element el = (Element)it.next();
                if (el!=element)
                    if ((el.getArea().intersect(this)))
                        return true;
            }
        }
        return(false);
    }
    
    
    public boolean translate(float x, float y){
        float backX = this.x;
        float backY = this.y;
        this.x = this.x + x;
        this.y = this.y + y;
        refreshValues();
        if (inMap()) {
            refreshIntersectedBoxes();
            return true;} else {
            this.x = backX;
            this.y = backY;
            refreshValues();
            return false;}
        
    }
    
    public boolean setX(float x){
        float backX = this.x;
        this.x = x;
        refreshValues();
        if (inMap()) {
            refreshIntersectedBoxes();
            return true;} else {
            this.x = backX;
            refreshValues();
            return false;}
        
    }
    
    public boolean setY(float y){
        float backY = this.y;
        this.y = y;
        refreshValues();
        if (inMap()) {
            refreshIntersectedBoxes();
            return true;} else {
            this.y = backY;
            refreshValues();
            return false;}
    }
    
    private void setSimuled(Vector v){
        this.lastX = this.x;
        this.lastY = this.y;
        this.lastAngle = angle;
        this.lastXlength = x_length;
        this.lastYlength = y_length;
        this.lastX0t = x0_t;
        this.lastY0t = y0_t;
        this.lastV = v;
    }
    
    
    public Vector simuleTranslate(float x, float y){
        float backX = this.x;
        float backY = this.y;
        this.x = this.x + x;
        this.y = this.y + y;
        refreshValues();
        if (!inMap()){
            this.x = backX;
            this.y = backY;
            setSimuled(intersectedBoxes);
            refreshValues();
            return null;
        }
        Vector v = computeIntersectedBoxes();
        setSimuled(v);
        this.x = backX;
        this.y = backY;
        refreshValues();
        return v;
    }
    
    public void confirmSimuled(){
        x = lastX;
        y = lastY;
        angle = lastAngle;
        x_length = lastXlength;
        y_length = lastYlength;
        x0_t = lastX0t;
        y0_t = lastY0t;
        unregisterBoxes();
        intersectedBoxes = lastV;
        registerBoxes();
        refreshValues();
    }
    
    
    private float toZero2PI(float f){ //retourne la valeur module 2Pi de f dans [0;2*Pi[.
        double t = Math.IEEEremainder(f, 2.*Math.PI);
        if (t<0) return (float)(t+2*Math.PI); else return (float)t;
    }
    
    public boolean rotate(float angle){ //angle de rotation dans [-2Pi;2Pi]
        float backAngle = this.angle;
        this.angle = toZero2PI(this.angle + angle);
        refreshValues();
        if (inMap()) {
            refreshIntersectedBoxes();
            return true;} else {
            this.angle = backAngle;
            refreshValues();
            return false;}
    }
    
    
    public boolean setAngle(float a){
        float backA = this.angle;
        this.angle = toZero2PI(a);
        refreshValues();
        if (inMap()) {
            refreshIntersectedBoxes();
            return true;} else {
            this.angle = backA;
            refreshValues();
            return false;}
        
    }
    
    
    public Vector simuleRotate(float angle){
        float backAngle = this.angle;
        this.angle = toZero2PI(this.angle + angle);
        refreshValues();
        if (!inMap()){
            this.angle = backAngle;
            setSimuled(intersectedBoxes);
            refreshValues();
            return null;
        }
        Vector v = computeIntersectedBoxes();
        setSimuled(v);
        this.angle = backAngle;
        refreshValues();
        return v;
    }
    
    public boolean rescale(float x, float y){
        float backXl = this.x_length;
        float backYl = this.y_length;
        float backx0t = this.x0_t;
        float backy0t = this.y0_t;
        this.x_length = this.x_length * x;
        this.y_length = this.y_length * y;
        this.x0_t = x0_t * x;
        this.y0_t = y0_t * y;
        refreshValues();
        if (inMap()) {
            refreshIntersectedBoxes();
            return true;} else {
            this.x_length = backXl;
            this.y_length = backYl;
            this.x0_t = backx0t;
            this.y0_t = backy0t;
            refreshValues();
            return false;}
    }
    
    public Vector simuleRescale(float xS, float yS){
        float backXl = this.x_length;
        float backYl = this.y_length;
        float backx0t = this.x0_t;
        float backy0t = this.y0_t;
        this.x_length = this.x_length * xS;
        this.y_length = this.y_length * yS;
        this.x0_t = x0_t * xS;
        this.y0_t = y0_t * yS;
        refreshValues();
        if (!inMap()){
            this.x_length = backXl;
            this.y_length = backYl;
            this.x0_t = backx0t;
            this.y0_t = backy0t;
            setSimuled(intersectedBoxes);
            refreshValues();
            return null;
        }
        Vector v = computeIntersectedBoxes();
        setSimuled(v);
        this.x_length = backXl;
        this.y_length = backYl;
        this.x0_t = backx0t;
        this.y0_t = backy0t;
        refreshValues();
        return v;
    }
    
    /**
     * Vérifie que le rectangle n'est pas sorti de la <code>Map</code>.
     */
    public boolean inMap() {
        float x1 = cos*(x0-x)-sin*(y0-y)+x;
        float y1 = cos*(y0-y)+sin*(x0-x)+y;
        float x2 = cos*(x0+x_length-x)-sin*(y0-y)+x;
        float y2 = cos*(y0-y)+sin*(x0+x_length-x)+y;
        float x3 = cos*(x0-x)-sin*(y0+y_length-y)+x;
        float y3 = cos*(y0+y_length-y)+sin*(x0-x)+y;
        float x4 = cos*(x0+x_length-x)-sin*(y0+y_length-y)+x;
        float y4 = cos*(y0+y_length-y)+sin*(x0+x_length-x)+y;
        return(x1>=0 & x2>=0 & x3>=0 & x4>=0 & y1>=0 & y2>=0 & y3>=0 & y4>=0 & x1<=xmax &x2<=xmax &x3<=xmax &x4<=xmax & y1<=ymax &y2<=ymax &y3<=ymax &y4<=ymax);
    }
    
    
    public void draw(Drawing g) { //fonction de dessin de la zone, qui dessine également l'image de l'élément
        if (element!=null)
            g.drawImage(element.getImage(),x0,y0,x_length,y_length,angle,x,y);
    }
    
    public void fill(Drawing g, Color c) {
        g.setColor(c);
        g.fillRect(x0,y0,x_length,y_length,angle,x,y);
    }
    
    public void drawBorder(Drawing g, Color c) {
        g.setColor(c);
        g.drawRect(x0,y0,x_length,y_length,angle,x,y);
    }
    
    public void highlight(Drawing g, Color c) {
        g.setColor(c);
        g.fillRect(x0,y0,x_length,y_length,angle,x,y);
        g.setColor(Color.BLACK);
        g.drawRect(x0,y0,x_length,y_length,angle,x,y);    }
    
    
    public String[][] getProperties() {
        String[][] ret = new String[6][2];
        String[] p1 = {"x",new Float(x).toString()};
        String[] p2 = {"y",new Float(y).toString()};
        String[] p3 = {"x_length",new Float(x_length).toString()};
        String[] p4 = {"y_length",new Float(y_length).toString()};
        String[] p5 = {"angle (in degrees)",new Float(180*angle/Math.PI).toString()};
        String[] p6 = {"intersected boxes",new Integer(intersectedBoxes.size()).toString()};
        ret[0] = p1; ret[1] = p2; ret[2] = p3; ret[3] = p4; ret[4] = p5; ret[5]=p6;
        return(ret);
    }
    
    public void setProperties(String s1, String s2) {
        try {
            if (s1.equals("x")) {
                setX(Float.parseFloat(s2));
            } else
                if (s1.equals("y")) {
                setY(Float.parseFloat(s2));
                } else
                    if (s1.equals("x_length")) {
                rescale(Float.parseFloat(s2)/x_length,1);
                    } else
                        if (s1.equals("y_length")) {
                rescale(1,Float.parseFloat(s2)/y_length);
                        } else
                            if (s1.equals("angle (in degrees)")) {
                setAngle(Float.parseFloat(s2)/180f*(float)Math.PI);
                            };
        } catch (Exception e) {}
    }
    
    public Object clone(){
        return new RectangularArea2(map, x, y, x_length, y_length, angle, x0_t, y0_t);
    }
    
}
