/**
   * 1. Votingverfahren fuer Strahl-Auswahl: Verfahren testet, ob der getroffene Strahl und der
   * Ausgangsstrahl die gleiche Richtung besitzen
   *
   * @param currentHit Treffer-Instanz, deren Strahlenrichtung ueberprueft wird
   * @param currentRay Strahlen-Instanz, fuer die ein Nachfolger gesucht wird
   * @return True, falls der Ausgangsstrahl die gleiche Richtung besitzt, wie der getroffene Strahl,
   *     False sonst
   */
  private boolean hasSameDirection(Hit currentHit, Ray currentRay) {

    MyVector3f currentHitDirection = currentHit.getHitRay().getDirection();
    MyVector3f currentRayDirection = currentRay.getDirection();

    // teste, ob die Strahlen parallel sind
    return mMathHelper.isParallel(currentHitDirection, currentRayDirection);
  }
  /**
   * 2. Votingverfahren fuer Strahlauswahl: Methode testet, ob der getroffene Strahl und der
   * Ausgangsstrahl Teil des gleichen Ausgangspolygons sind
   *
   * @param currentHit Treffer-Instanz, fuer deren Strahl getestet wird, ob dieser zum gleichen
   *     Polygon gehoert, wie der Ausgangsstrahl
   * @param currentRay Strahlen-Instanz, fuer die ein Nachfolger gesucht wird
   * @param bucket Eimer, fuer den aktuell ein gemergter Grundriss errechnet wird
   * @return True, falls beide Strahlen Kanten des gleichen Polygons sind, False sonst
   */
  private boolean isPartOfSameObject(
      final Hit currentHit, final Ray currentRay, final FootprintBucket bucket) {

    // bestimme die Quellpolygone fuer den Trefferstrahl und den Teststrahl
    List<Footprint> footprints = bucket.getFootprints();
    MyPolygon currentPolygon = null;
    Ray hitRay = currentHit.getHitRay();

    LOGGER.debug("CURRENT RAY: " + currentRay + " HITRAY: " + hitRay);

    for (int i = 0; i < footprints.size(); i++) {
      currentPolygon = footprints.get(i).getFootprintPoly();

      // wenn beide Strahlen im Polygon enthalten sind, gebe True zurueck
      if (currentPolygon.isRayInPolygon(hitRay) && currentPolygon.isRayInPolygon(currentRay)) {
        LOGGER.debug("SAME POLY");
        LOGGER.debug("CUR POLY: " + currentPolygon);
        return true;
      }
    }
    return false;
  }
  /**
   * Methode berechnet fuer den uebergebenen Grundriss-Bucket einen gemergeten Grundriss und gibt
   * diesen als Vektor von Vertex3d-Instanzen zurueck.
   *
   * @param bucket Bucket mit Grundrissen, die innerhalb der Methode gemerged werden
   * @return Vektor mit Vertex3d-Instanzen, die den gemergten Grundriss beschreiben
   */
  public List<Vertex3d> computeMergedFootprint(FootprintBucket bucket) {

    LOGGER.info("Footprintbucket enthaelt: " + bucket.getFootprints().size() + " Eingabepolygone!");
    Ray startRay = findStartRay(bucket);

    LOGGER.trace("Startray fuer Schnittberechnungen: " + startRay);
    assert startRay != null : "FEHLER: Es konnte kein Anfangsstrahl ermittelt werden!";

    List<Vertex3d> resultVertices = new ArrayList<Vertex3d>();

    // sammele erneut alle Strahlen aller Grundrisse ein
    List<Ray> allRays = new ArrayList<Ray>();
    List<Footprint> allFootprints = bucket.getFootprints();
    Iterator<Footprint> footprintIter = allFootprints.iterator();
    while (footprintIter.hasNext()) {
      allRays.addAll(footprintIter.next().getRays());
    }

    Iterator<Ray> rayIter = allRays.iterator();
    LOGGER.trace("Insgesamt befinden sich " + allRays.size() + " Rays im Eimer");

    while (rayIter.hasNext()) {
      LOGGER.trace(rayIter.next());
    }

    Ray currentRay = null, lastRay = null, intersectionRay = null;
    MyVector3f intersectionPoint = null;
    Vertex3d resultVertex = null;

    // fuege Startpunkt des Startstrahls als erstes Vertex hinzu
    resultVertex = new Vertex3d(startRay.getStart());
    resultVertices.add(resultVertex);

    // Steuervariable fuer Iterationen
    Boolean doContinue = true;

    // erster Treffer des Startstrahls, wird fuer Abbruchkriterium benoetigt
    MyVector3f firstHit = null;

    // Iteration laeuft so lange, bis Abbruchkriterium erfuellt wurde
    while (doContinue) {
      // 1. Iteration
      if (currentRay == null) {
        currentRay = startRay;
      }

      Hit resultHit = findIntersectingRay(currentRay, bucket, lastRay, intersectionPoint);

      assert resultHit != null : "Es konnte kein Schnittstrahl ermittelt werden";
      // System.out.println("Gefundener Strahl: " + intersectionRay);

      intersectionRay = resultHit.getHitRay();
      intersectionPoint = resultHit.getIntersection();

      // speichere den ersten gefundenen Treffer
      if (firstHit == null) firstHit = intersectionPoint;

      // erzeuge eine Vertex3d-Instanz fuer den Schnittpunkt und fuege sie
      // zum Result-Vektor hinzu
      resultVertex = new Vertex3d(intersectionPoint);

      // breche ab, wenn ein Vertex geadded werden soll, das bereits
      // vorhanden ist
      LOGGER.trace("Adde Vertex: " + resultVertex);
      if (resultVertices.contains(resultVertex)) break;
      else resultVertices.add(resultVertex);

      // naechste Iteration vorbereiten
      lastRay = currentRay;
      currentRay = intersectionRay;

      // Abbruchkriterium pruefen
      doContinue = checkTermination(startRay, firstHit, currentRay, intersectionPoint);
    }

    return resultVertices;
  }