/**
 * DenkXweb, Version 1.0.0
 * Copyright (c) 2005 rjm business solutions GmbH
 * All rights reserved, license restrictions apply
 *
 * $Id$
 *
 * Geometriedefinition und -Verarbeitung
 */


/**
 * Class Point
 * 
 * Ein Punkt in der Ebene, spezifiziert durch seinen Abstand zum
 * Ursprung.
 * 
 * @attr x  Rechtswert
 * @attr y  Hochwert 
 */

/** 
 * Konstruktur für "Point": Legt einen neuen Punkt an der angegebenen
 * Position an.
 *
 * @param anX  Ausgangswert für den Rechtswert
 * @param anY  Ausgangswert für den Hochwert
 */
function Point(anX, anY) {
  this.x = anX;
  this.y = anY;
  
  /** 
   * Bewegt diesen Punkt an eine neue Position in der Ebene.
   * @param newX  Neuer Rechtswert
   * @param newY  Neuer Hochwert
   * @return this zur Befehlsverkettung
   */
  this.move = function(newX, newY) {
    this.x = newX;
    this.y = newY;
    return this;
  };
    
  /** 
   * Übernimmt die Position eines anderen Punktes.
   * @param otherPt  Ein Objekt vom Typ "Point"
   * @return this zur Befehlsverkettung
   */
  this.assign = function(otherPt) {
    this.move(otherPt.x, otherPt.y);
    return this;  // Befehlsverkettung erlauben
  };
    
  /** 
   * Verschiebt diesen Punkt an eine neue Positon
   * @param deltaX  Abstand Rechtswert zwischen alter und neuer Position.
   * @param deltaY  Analog deltaX für Hochwert.
   * @return this zur Befehlsverkettung
   */
  this.translate = function(deltaX, deltaY) {
    this.x += deltaX;
    this.y += deltaY;
    return this;  // Befehlsverkettung erlauben
  };
    
  /**
   * Überprüft, ob sich dieser Punkt innerhalb der angegebenen Grenzen
   * liegt und verschiebt ihn andernfalls in den gültigen Bereich.

   * @param min  Linke obere Ecke des begrenzenden Rechtecks, vom Typ
   *             "Point"
   * @param max  Rechte untere Ecke des begrenzenden Rechtecks, vom Typ
   *             "Point"
   * @return this zur Befehlsverkettung
   */
  this.checkBounds = function(min, max) {
    this.x = Math.min(max.x, Math.max(min.x, this.x));
    this.y = Math.min(max.y, Math.max(min.y, this.y));
    return this;  // Befehlsverkettung erlauben
  };

}


/**
 * Class Polygon
 * 
 * @attr outerRing   äußere Begrenzung, enthält Objekte vom Typ Point
 * @attr innerRings  innere Begrenzungen, Arrays vom Typ Point
 */

/** 
 * Konstruktur für "Polygon": Legt ein neues Polygon an.
 *
 * @param outerRing   Äußere Begrenzung als Koordinaten-Vektor
 * @param innerRings  (optional) Innere Begrenzungen, wenn vorhanden,
 *                    angegeben als Vektor von Koordinaten-Vektoren
 */
function Polygon(outerRing, innerRings) {
  
  /**
   * Hilfsfunktion zum Konvertieren eines Koordinaten-Vektors in ein
   * Array von Punkten
   *
   * @param   coords Array mit Koordinatenangaben (muss daher gerade 
   *          Anzahl von Elementen enthalten)
   * @return  Array mit Objekten vom Typ Point
   */
  function coordsToPoints(coords) {
    var result = new Array(coords.length / 2);
    for (var i = 0; i < result.length; i++)
      result[i] = new Point(coords[i*2], coords[i*2+1]);
    return result;
  }

  this.outerRing = coordsToPoints(outerRing);
  if (innerRings != null) {
    this.innerRings = new Array(innerRings.length);
    for (var i = 0; i < innerRings.length; i++)
      this.innerRings[i] = coordsToPoints(innerRings[i]);
  } else
    this.innerRings = null;


  // testRing-Unterfunktion aus contains(p) herausgezogen wg.
  // TraceMonkey-Fehler in FF 3.5.0
  function testRing(p, c, ring) {
    for (var i = 0, j = ring.length-2; i <  ring.length-1; j = i++) {
      if ((((ring[i].y <= p.y) && (p.y < ring[j].y)) ||
           ((ring[j].y <= p.y) && (p.y < ring[i].y))) &&
          (p.x < (ring[j].x - ring[i].x)*
           (p.y - ring[i].y)/(ring[j].y - ring[i].y) + ring[i].x))
        c = !c;
    }
    return c;
  }

  /**
   * Testet, ob der angegebene Punkt vom Typ Point in diesem Polygon
   * enthalten ist.
   *
   * @param  p  Punkt, der überprüft werden soll, vom Typ Point
   * @return true, wenn Punkt in Polygon, false sonst
   */
  this.contains = function(p) {
    var c = testRing(p, false, this.outerRing);
    if (innerRings != null)
      for (var i = 0; i < innerRings.length; i++)
        c = testRing(p, c, this.innerRings[i]);

    return c;
  }

}



