
// PointHeight.java
// Andrew Davison, April 2005, ad@fivedots.coe.psu.ac.th

/*  Načte soubor se čtyřmi souřadnicemi, které definuje jednotkový
    čtverec v rovině XZ, ovšem s různými výškami (y-ové souřadnice). 
    Tyto souřadnice jsou označeny písmeny P, Q, R a S počínaje levým
    dolním rohem a jdoucí proti směru hodinových ručiček při pohledu
    shora.

    Dále čte dvojice (x,z) umístěné někde uvnitř tohoto čtverce.

    Vypočítá _dvě_ možné výšky těchto bodů (x,z), a to v závislosti
    na triangulaci čtverce.
    (Podél úseček PR a SQ lze triangulaci provést  dvěma možnými způsoby.)
*/

import java.util.*;
import java.io.*;
import java.text.DecimalFormat;

import javax.vecmath.*;



public class PointHeight
{
  // říká, je-li bod nad nebo pod triangulační úsečkou
  private static final int ABOVE = 0;
  private static final int BELOW = 1;

  private DecimalFormat df;    // pro formátování výstupu

  private Vector3d coordm, coordQ, coordR, coordS, pt;
  private int posSQ, posPR; 
   /* Daný bod může být nad nebo pod triangulační úsečkou
      definovanou body SQ a PR.
      Proměnné posSQ a posPR nabívají hodnot ABOVE nebo BELOW.
   */


  public PointHeight(String fnm)
  {
    df = new DecimalFormat("0.###");  // 3 dm

    getFileCoords(fnm);

    if (inRange(pt)) {
      relativeToLines(pt);
      calcHeightSQ(posSQ, pt);   // pt relationship with SQ
      calcHeightPR(posPR, pt);   // pt relationship with PR
    }
    else
      System.out.println("Daný bod se nachází mimo oblast definovanou body PQRS");
  
  } // konec metody: PointHeight()



  private void getFileCoords(String fnm)
  /* Získá údaje o souřadnicích a bodu ze souboru fnm, jehož formát vypadá takto:
        num num num     // souřadnice P
        num num num     // souřadnice Q
        num num num     // souřadnice R
        num num num     // souřadnice S
        num num         // souřadnice bodu (x,z)
  */
  { try {
      BufferedReader br = new BufferedReader( new FileReader(fnm));
      coordm = getCoord('P', br.readLine() );   
      coordQ = getCoord('Q', br.readLine() );   
      coordR = getCoord('R', br.readLine() );   
      coordS = getCoord('S', br.readLine() );

      pt = getPoint( br.readLine() );

      br.close();
    } 
    catch (IOException e) 
    { System.out.println("Chyba při čtení souboru: " + fnm);
      System.exit(1);
    }
  }  // konec metody: getFileCoords()



  private Vector3d getCoord(char ptCh, String line)
  // vrátí (x,y,z)
  {
    double vals[] = new double[3];            // pro data souřadnice
    vals[0] = 0; vals[1] = 0; vals[2] = 0;    // představuje (x,y,z)

    StringTokenizer tokens = new StringTokenizer(line);
    String token;
    int count = 0;
    while (tokens.hasMoreTokens() && (count < 3)) {
      token = tokens.nextToken();
      try {
        vals[count] = Double.parseDouble(token);
        count++;
      }
      catch (NumberFormatException ex){ 
        System.out.println("Nesprávný formát " + ptCh); 
        break;
      }
    }
    if (count != 3)
      System.out.println("Nesprávná data " + ptCh);

    Vector3d pt = new Vector3d( vals[0], vals[1], vals[2]);
    System.out.println(ptCh + " coord: (" + df.format(pt.x) +
				", " + df.format(pt.y) + ", " + df.format(pt.z) + ")" );
    return pt;
  }  // konec metody: getCoord()



  private Vector3d getPoint(String line)
  // vrátí (x,0,z); 0 se nevyužije
  {
    double vals[] = new double[2];  // pro (x,z)-data ze vstupu
    vals[0] = 0; vals[1] = 0;

    StringTokenizer tokens = new StringTokenizer(line);
    String token;
    int count = 0;
    while (tokens.hasMoreTokens() && (count < 2)) {
      token = tokens.nextToken();
      try {
        vals[count] = Double.parseDouble(token);
        count++;
      }
      catch (NumberFormatException ex){ 
        System.out.println("Nesprávný formát bodu"); 
        break;
      }
    }
    if (count != 2)
      System.out.println("Nesprávný formát bodu");

    Vector3d pt = new Vector3d( vals[0], 0, vals[1]);  // libovolná y-ová hodnota
    System.out.println("Point: (" + df.format(pt.x) + ", " + df.format(pt.z) + ")" );
    return pt;
  }  // konec metody: getCoord()



  private boolean inRange(Vector3d pt)
  // je bod uvnitř čtyřúhelníka definovaného obdy ABCD
  {
    if ((pt.x < coordS.x) || (pt.x > coordQ.x) || 
        (pt.z < coordS.z) || (pt.z > coordQ.z))
      return false;
    return true;
  }  // konec metody: inRange()



  private void relativeToLines(Vector3d pt)
  /* Rozhodne, je-li bod pt nad nebo pod triangulačními úsečkami
     DB a AC.
  */
  {
    // otestuj úsečku SQ: z = x + (S.z-S.x)
    if (pt.z > (pt.x + coordS.z - coordS.x))
      posSQ = ABOVE;
    else
      posSQ = BELOW;

    // otestuj úsečku PR:  z = -x + (P.z+P.x)
    if (pt.z > (-pt.x + coordm.z + coordm.x))
      posPR = ABOVE;
    else
      posPR = BELOW;

    String relSQ = (posSQ == ABOVE) ? "Above " : "Below ";
    String relPR = (posPR == ABOVE) ? "Above " : "Below ";
    System.out.println("Line Relationships: " + relSQ + "SQ;  " + relPR + "PR");
  } // konec metody: relativeToLines()



  private void calcHeightSQ(int linePos, Vector3d pt)
  /* Vypočítá dva vektory u a v, které definují trojúhelník,
     jenž používá triangulační úsečku SQ
     
     Bod pt je nad/pod úsečkou SQ, což nám řekne parametr linePos.   
  */
  {
    Vector3d u = new Vector3d();
    Vector3d v = new Vector3d();

    if (linePos == BELOW) {
      System.out.println("\nSQ Trojúhelník SQR");
      u.set( 1, coordQ.y-coordS.y, 1);    // Q-S
      v.set( 1, coordR.y-coordS.y, 0);    // R-S
      vecsToHeight(u, v, coordS);
    }
    else if (linePos == ABOVE) {
      System.out.println("\nSQ Torjúhelník SPQ");
      u.set( 1, coordQ.y-coordm.y, 0);     // Q-P
      v.set( 0, coordS.y-coordm.y, -1);    // S-P
      vecsToHeight(u, v, coordm);
    }
    else
      System.out.println("Chyba calcHeightSQ()");
  }  // konec metody: calcHeightSQ()



  private void calcHeightPR(int linePos, Vector3d pt)
 /* Vypočítá dva vektory u a v, které definují trojúhelník,
     jenž používá triangulační úsečku PR
     
     Bod pt je nad/pod úsečkou PR, což nám řekne parametr linePos.   
  */
  {
    Vector3d u = new Vector3d();
    Vector3d v = new Vector3d();

    if (linePos == BELOW) {
      System.out.println("\nPR Trojúhelník PRS");
      u.set( 1, coordR.y-coordm.y, -1);     // R-P
      v.set( 0, coordS.y-coordm.y, -1);     // S-P
      vecsToHeight(u, v, coordm);
    }
    else if (linePos == ABOVE) {
      System.out.println("\nPR Trojúhelník RPQ");
      u.set( 1, coordQ.y-coordm.y, 0);     // Q-P
      v.set( 1, coordR.y-coordm.y, -1);    // R-P
      vecsToHeight(u, v, coordm);
    }
    else
      System.out.println("chyba calcHeightPR()");
  }  // konec metody: calcHeightPR()



  private void vecsToHeight(Vector3d u, Vector3d v, Vector3d pt)
  /* Vypočítá normálový vektor vektorů u a v.

     Vypočítá vzdálenost k počátku pomocí skalárního součinu
     normálového vektoru a bodu v trojúhelníku.

     Normálový vektor je (A,B,C) a vzdálenost D.
     Tyto koeficienty definují rovnici trojúhelníka:
            (A * x) + (B * y) + (C * z) = D

     Pomocí této rovnice vypočítá y-ovou hodnotu v bodu (x,z).
  */
  {
    Vector3d normal = new Vector3d();
    normal.cross(u, v);
    normal.normalize();
    double dist = normal.dot(pt);

    System.out.println("A: "+ df.format(normal.x) + ", B: " +
			df.format(normal.y) + ", C: " + df.format(normal.z) +
			", D: " + df.format(dist) );    // Ax + By + Cz = D

    double height = (dist - (normal.x * pt.x) - (normal.z * pt.z)) / normal.y;
    System.out.println(" Výška bodu: " + df.format(height) );
  }  // konec metody: vecsToHeight()



  // ---------------------- main() -----------------------

  public static void main(String args[])
  {
    if (args.length != 1) {
      System.out.println( "Použití: java PointHeight <file>");
      System.exit(0);
    }
    new PointHeight(args[0]);
  } // konec metody: main()



} // konec třídy: PointHeight