
// Triangles.java
// Martin Davis
// The LocationTech Technology (LTT) Group; Vivid Solutions
// https://github.com/locationtech/jts
// Modified by Andrew Davison, ad@coe.psu.ac.th, March 2023

/* Illustrates various computational geometry calculations
   commonly used with triangles, mostly by using the
   Triangle class:
     * interior angles
     * acuteness tests for CCW
         Note: GeoViewer uses a JPanel and so the y-axis runs downwards;
               this means that clockwise points appear to be 
               drawn in a CCW order!
     * lengths, perimeter, area

     * calculation of centers: centroid, circumcenter, orthocenter, incenter
        -- illustration of Euler's line

     * uses for Vector2D

   The triangle is created from three randomly generated points
   by using RandomPointsBuilder.

   Usage:
    > compile Triangles.java
    > run Triangles
*/

import java.awt.Color;

import org.locationtech.jts.geom.*;
import org.locationtech.jts.algorithm.*;
import org.locationtech.jts.shape.random.*;
import org.locationtech.jts.algorithm.match.*;

import org.locationtech.jts.math.Vector2D;



public class Triangles
{

  public static void main(String[] args)
  {
    GeomsViewer gv = Utils.view("Triangles");

    GeometryFactory gf = new GeometryFactory();
    CoordinateSequenceFactory coordsFac = gf.getCoordinateSequenceFactory();

    RandomPointsBuilder builder = new RandomPointsBuilder();
    builder.setNumPoints(3);
    builder.setExtent( new Envelope(0, gv.getWidth(), 0, gv.getHeight()));
    Geometry pts = builder.getGeometry();   // polygon made from 3 random pts

    CoordinateSequence coordSeq = CoordinateSequences.ensureValidRing(coordsFac, 
                                        coordsFac.create(pts.getCoordinates()) );
    if (Orientation.isCCW(coordSeq))
      CoordinateSequences.reverse(coordSeq);  // put coordinates in clockwise order

    Geometry triGeom = gf.createPolygon(coordSeq);
    System.out.println("Geometry: " + Utils.simplify(triGeom));
    gv.add(triGeom);

    Coordinate[] coords = coordSeq.toCoordinateArray();
    Triangle tri = new Triangle(coords[0], coords[1], coords[2]);
       // use a Triangle from than a Geometry

    System.out.println("Triangle: " + Utils.sTriangle(tri));

    System.out.println("Vertices:");
    System.out.println("  0) " + Utils.sCoord(tri.p0));
    gv.add("Pt 0", Utils.makePoint(tri.p0));

    System.out.println("  1) " + Utils.sCoord(tri.p1));
    gv.add("Pt 1", Utils.makePoint(tri.p1));

    System.out.println("  2) " + Utils.sCoord(tri.p2));
    gv.add("Pt 2", Utils.makePoint(tri.p2));

    System.out.println("Interior Angles:");
    System.out.printf("  0) %.1f\n", 
          Angle.toDegrees(Angle.interiorAngle(tri.p2, tri.p0, tri.p1)) );
    System.out.printf("  1) %.1f\n",
           Angle.toDegrees(Angle.interiorAngle(tri.p0, tri.p1, tri.p2)) );

     /* code calculates angle around middle argument based on prev and next vectors
          interiorAngle(p0, p1, p2):
              anglePrev = Angle.angle(p1, p0);
              angleNext = Angle.angle(p1, p2);
              return normalizePositive(angleNext - anglePrev);
     */

    System.out.printf("  2) %.1f\n",
          Angle.toDegrees(Angle.interiorAngle(tri.p1, tri.p2, tri.p0)) );

    System.out.println("Is acute? " + tri.isAcute() + "; is CCW? " + tri.isCCW());

    System.out.println("Side lengths:");
    System.out.printf("  0-to-1 ) %.1f\n", tri.p0.distance(tri.p1) );
    System.out.printf("  1-to-2 ) %.1f\n", tri.p1.distance(tri.p2) );
    System.out.printf("  2-to-0 ) %.1f\n", tri.p2.distance(tri.p0) );
    System.out.printf("Longest side = %.1f\n", tri.longestSideLength());
    System.out.printf("Perimeter length = %.1f\n", tri.length());
    System.out.printf("Area = %.2f\n", tri.area());

    System.out.println("\nCenters:");
    Coordinate centroid = tri.centroid();
    System.out.println(" Centroid (red) = " + Utils.sCoord(centroid));
    gv.add("centroid", gf.createPoint(centroid), Color.RED);

    Coordinate circumcenter = tri.circumcentre();
    System.out.println(" Circumcenter (green) = " + Utils.sCoord(circumcenter));
    gv.add("circumcenter", gf.createPoint(circumcenter), Color.GREEN);

    Coordinate orthocenter = Utils.orthocenter(tri);
    System.out.println(" Orthocenter (blue) = " + Utils.sCoord(orthocenter));
       // note: if the triangle is very flat, then this point may be outside the GeoViewer window
    gv.add("orthocenter", gf.createPoint(orthocenter), Color.BLUE);

    System.out.println(" Incenter = " + Utils.sCoord(tri.inCentre()));

    Coordinate bisect01 = Triangle.angleBisector(tri.p0, tri.p2, tri.p1);
    System.out.println("Angle Bisector of side 0-to-1 (magenta) = " + Utils.sCoord(bisect01));
    gv.add("angBisector", gf.createPoint(bisect01), Color.MAGENTA);

    System.out.printf("\nDist from centroid to circumcenter (GO) = %.1f\n",  
                                                       centroid.distance(circumcenter));
    System.out.printf("Dist from centroid to orthocenter (GH) = %.1f\n",  
                                                       centroid.distance(orthocenter));
    System.out.printf("On Euler's line GH=2GO: %.2f =?= %.2f\n", 
                   centroid.distance(orthocenter), 2*centroid.distance(circumcenter));

    // uses for Vector2D
    System.out.println();
    Vector2D v01 = new Vector2D(tri.p0, tri.p1);  // from -> to
    Vector2D v02 = new Vector2D(tri.p0, tri.p2);  // from -> to
    double angle0 = Math.toDegrees(Math.acos(v01.dot(v02)/(v01.length() * v02.length())));
    System.out.printf("Interior angle of vertex0: %.1f\n", angle0);

    System.out.printf("Triangle area: %.2f\n", Math.abs(Utils.cross(v01, v02)/2));
  }  // end of main()


}  // end of Triangles class
