简体   繁体   中英

Need some assistance creating a Triangle class for Java

I'm supposed to make my own Java Triangle class for my project at school. A new Triangle takes the (x,y) coordinates of the 3 points in order from left to right. My concern is mainly with the way I'm making sideAB, sideBC, and sideAC instance variables and them initializing them in the contructor. Also, the maxAngle and minAngle looks like a mess and what I would like it to have angleA angleB and angleC variables similar to the way I have the sides. Also, im not sure if any of my variables are supposed to be static. I'm sorry if my code is really noobie but this is my first Java project for my class.

Here's what I have. It works but I don't think I'm doing everything the correct way:

public class Triangle {
    private double ax;
    private double ay;
    private double bx;
    private double by;
    private double cx;
    private double cy;
    //added these variables because I use them so frequently when calculating angles, area, perimeter, etc.
    private double sideAB;
    private double sideBC;
    private double sideAC;

    public Triangle(double x1, double y1, double x2, double y2, double x3, double y3)
    {
        ax = x1;
        ay = y1;
        bx = x2;
        by = y2;
        cx = x3;
        cy = y3;
        sideAB= Math.abs(Math.sqrt(Math.pow(bx-ax,  2)+Math.pow(by-ay, 2)));
        sideBC= Math.abs(Math.sqrt(Math.pow(cx-bx,  2)+Math.pow(cy-by, 2)));
        sideAC= Math.abs(Math.sqrt(Math.pow(cx-ax,  2)+Math.pow(cy-ay, 2)));
    }
    public double getPerimeter()
    {
        //add the 3 sides together for the perimeter
        double perimeter = sideAB + sideBC + sideAC;
        return perimeter;
    }
    public double getArea()
    {
        //used Heron's formula to find the area of the triangle
        double s = (sideAB + sideBC + sideAC)/2;
        double area = Math.sqrt(s*(s - sideAB)*(s - sideBC)*(s - sideAC));
        return area;
    }
    public double getSideAB()
    {
        return sideAB;
    }
    public double getSideBC()
    {
        return sideBC;
    }
    public double getSideAC()
    {
        return sideAC;
    }
    public double getAngleC()
    {
        //Law of cosines to find the angle
        double a2 = Math.pow(sideAB, 2);
        double b2 = Math.pow(sideBC, 2);
        double c2 = Math.pow(sideAC, 2);
        double cosC = ((b2 + c2)-a2)/((2*sideBC)*sideAC);
        double angleC = Math.acos(cosC);
        angleC = Math.toDegrees(angleC);
        return angleC;
    }
    public double getAngleB()
    {
        double a2 = Math.pow(sideAB, 2);
        double b2 = Math.pow(sideBC, 2);
        double c2 = Math.pow(sideAC, 2);
        double cosB = ((a2+b2-c2)/(2*sideAB*sideBC));
        double angleB = Math.acos(cosB);
        angleB = Math.toDegrees(angleB);
        return angleB;
    }
    public double getAngleA()
    {
        double a2 = Math.pow(sideAB, 2);
        double b2 = Math.pow(sideBC, 2);
        double c2 = Math.pow(sideAC, 2);
        double cosA = ((a2+c2-b2)/(2*sideAB*sideAC));
        double angleA = Math.acos(cosA);
        angleA = Math.toDegrees(angleA);
        return angleA;
    }
    public double maxSide()
    {
        //if-else if-else statements for max and min sides functions
        if (sideAB >= sideBC && sideAB >= sideAC)
        {
            return sideAB;
        }
        else if(sideBC >= sideAB && sideBC >= sideAC)
        {
            return sideBC;
        }
        else
        {
            return sideAC;
        }
    }
    public double minSide()
    {
        if (sideAB <= sideBC && sideAB <= sideAC)
        {
            return sideAB;
        }
        else if(sideBC <= sideAB && sideBC <= sideAC)
        {
            return sideBC;
        }
        else
        {
            return sideAC;
        }
    }
    public double maxAngle()
    {
        double a2 = Math.pow(sideAB, 2);
        double b2 = Math.pow(sideBC, 2);
        double c2 = Math.pow(sideAC, 2);
        double cosC = ((b2 + c2)-a2)/((2*sideBC)*sideAC);
        double angleC = Math.acos(cosC);
        angleC = Math.toDegrees(angleC);
        double cosB = ((a2+b2-c2)/(2*sideAB*sideBC));
        double angleB = Math.acos(cosB);
        angleB = Math.toDegrees(angleB);
        double cosA = ((a2+c2-b2)/(2*sideAB*sideAC));
        double angleA = Math.acos(cosA);
        angleA = Math.toDegrees(angleA);
        if (angleA >= angleB && angleA >= angleC)
        {
            return angleA;
        }
        else if(angleB >= angleA && angleB >= angleC)
        {
            return angleB;
        }
        else
        {
            return angleC;
        }       
    }
    public double minAngle()
    {
        double a2 = Math.pow(sideAB, 2);
        double b2 = Math.pow(sideBC, 2);
        double c2 = Math.pow(sideAC, 2);
        double cosC = ((b2 + c2)-a2)/((2*sideBC)*sideAC);
        double angleC = Math.acos(cosC);
        angleC = Math.toDegrees(angleC);
        double cosB = ((a2+b2-c2)/(2*sideAB*sideBC));
        double angleB = Math.acos(cosB);
        angleB = Math.toDegrees(angleB);
        double cosA = ((a2+c2-b2)/(2*sideAB*sideAC));
        double angleA = Math.acos(cosA);
        angleA = Math.toDegrees(angleA);
        if (angleA <= angleB && angleA <= angleC)
        {
            return angleA;
        }
        else if(angleB <= angleA && angleB <= angleC)
        {
            return angleB;
        }
        else
        {
            return angleC;
        }       
    }
}

The salient properties of a triangle (at least in two-dimensional Euclidean space) are its three points.

That's it, that's all you need to store. Everything else can be calculated from that. You don't need the lengths of the sides or the angles of the vertices, they can all be derived from those three points.

My advice would be to simply create a Point type and then build your Triangle type from three of those.

If you establish that the calculation of these derived values is too expensive then, and only then, should you think about caching that information. But I doubt that will be the case. If it does turn out to be the case, there are (at least) two approaches.

First is to calculate the derived values whenever the non-derived values change. This has the advantage of ensuring that all values are up-to-date whenever you change something and your code is simplified (every setSomething() method, and the constructor, simply calls the calcAllDerivedValues() method).

Secondly, you can mark the derived values as dirty whenever you change the non-derived ones. Then whenever you need derived data, the methods that calculate it can check if they're dirty then calculate (and cache) them if so.

If they're not dirty, they simply return the cached values. This is a little more complex but can remove unnecessary calculations, especially if you have one dirty flag per derived value - you only calculate what you need, when you need it.

And, for your specific question on static you only use static class-level variables if they are shared amongst all instances. Since the angles or the lengths of the sides are specific to one instance, they should not be static.


Here's how I'd start. First, a Point class which has x and y members (along with getters and setters for them), and the ability to work out the distance and angle (relative to a fixed angle like "straight up") to another point, something like:

private double x, y;

public double getX();
public double getY();
public void setX(double newX);
public void setY(double newY);
public void setXY(double newX, double newY);
public double getDistance (Point other);
public double getAngle (Point other);

Then, your Triangle class needs three of those points:

private Point a, b, c;

along with appropriate setters and getters, and whatever functions you need to calculate the desired derived properties.

That would be things like:

  • getting the perimeter by working out the distance between a->b , b->c and a->c (done with the Point class getDistance() method) then adding them.
  • using that same information (the three line lengths) to get the area.
  • getting the angles based on differences between results of the Point class getAngle() for (as an example) a->b and a->c (for angle A). If this angle is greater than 180, obviously that's the outside of the triangle and you should subtract it from 360 to get the inner angle.

And you don't need to duplicate all the code for each situation. For example, you want to be able to work out the angle at any of the three vertices. You don't need to duplicate all the intelligent code for each one.

Instead write that complex code once and call it in three different ways.

By that I mean something like:

// Can just call Point stuff directly for distances (simple code).
double getDistAB() { return a.getDistance (b); }
double getDistAC() { return a.getDistance (c); }
double getDistBC() { return b.getDistance (c); }

double getPerimeter() { return getDistAB() + getDistAC() + getDistBC(); }

// Returns the angle inside triangle at the first vertex (complex code).
double getAngleAtPointX (Point x, Point y, Point z) {
    double angle = x.getAngle (y) - x.getAngle (z);
    if (angle < 0)
        angle = -angle;
    if (angle > 180)
        angle = 360 - angle;
    return angle;
}

// Then just call that with different arguments.
double getAngleA() { return getAngleAtPoint (a, b, c); }
double getAngleB() { return getAngleAtPoint (b, a, c); }
double getAngleC() { return getAngleAtPoint (c, a, b); }

For the most part, the code is correct.

The biggest problem, though, is that you write code that does the same thing multiple times. For example this method:

public double maxAngle()
{
    double a2 = Math.pow(sideAB, 2);
    double b2 = Math.pow(sideBC, 2);
    double c2 = Math.pow(sideAC, 2);
    double cosC = ((b2 + c2)-a2)/((2*sideBC)*sideAC);
    double angleC = Math.acos(cosC);
    angleC = Math.toDegrees(angleC);
    double cosB = ((a2+b2-c2)/(2*sideAB*sideBC));
    double angleB = Math.acos(cosB);
    angleB = Math.toDegrees(angleB);
    double cosA = ((a2+c2-b2)/(2*sideAB*sideAC));
    double angleA = Math.acos(cosA);
    angleA = Math.toDegrees(angleA);
    if (angleA >= angleB && angleA >= angleC)
    {
        return angleA;
    }
    else if(angleB >= angleA && angleB >= angleC)
    {
        return angleB;
    }
    else
    {
        return angleC;
    }       
}

computes the max angle but you could have used the already implemented methods getAngleA() , getAngleB() and getAngleC() to write this one like this:

public double maxAngle() {
  if(getAngleA() => getAngleB() && getAngleA() => getAngleC())
    return getAngleA();
  if(getAngleB() => getAngleA() && getAngleB() => getAngleC())
    return getAngleB();
  return getAngleC();
}

If performance is of importance, you should pre-calculate all values (asuming one cannot change any point of the triangle), especially the angles, if they are accessed more than once.

And no, you should definitely not use any static variables, as these would be shared by every instance of your Triangle class.

Some of the modification that can be done in your code are: -

  • Create a Coordinate class to store x, and y coordinates of three coordinate of triangle.
  • Create a Side class to store all the three sides.
  • Create an Angle class to find and store all the angles..

  • Now rather than having to create everything in your one class, you can have them calculated in their respective class.. And just fetch from there..

  • EG: - Let a particular Angle instance find out its own angle , and you can just get the angle from there.. So, you won't have to write the logic to create angle thrice..

  • You can move your findMaxSide() , and findMinSide() code to your Side class.. Because your method should ideally be in the class containing the information that your method is using..

  • Similarly move findMaxAngle() and findMinAngle() to your Angle class.

So, Here I am giving you Angle class that you can use.. You can create your Coordinate and Side class on your own : -

public class Angle {

    private double angle;

    public Angle() {

    }

    public double getAngle() {
        return this.angle;
    }

    public void setAngle(Side side1, Side side2, Side side3) {

        double a2 = Math.pow(side1.getLength(), 2);
        double b2 = Math.pow(side2.getLength(), 2);
        double c2 = Math.pow(side3.getLength(), 2);
        double cosB = ((a2+b2-c2)/(2*side1.getLength()*side2.getLength()));

        double tempAngle = Math.acos(cosB);

        this.angle = Math.toDegrees(tempAngle);
    }

    public Angle maxAngle(Angle angle1, Angle angle2) {

        Angle temp = angle1.getAngle() > angle2.getAngle() ? angle1 : angle2;
        return temp.getAngle() > this.getAngle() ? temp : this;
    }

    public Angle minAngle(Angle angle1, Angle angle2) {

        Angle temp = angle1.getAngle() < angle2.getAngle() ? angle1 : angle2;
        return temp.getAngle() < this.getAngle() ? temp : this;
    }
}

side1 and side2 are the sides between which you want to find angle..

You can use this class from your Triangle class as follows: -

public class Triangle {

    private Coordinate a;
    private Coordinate b;
    private Coordinate c;
    //added these variables because I use them so frequently when calculating angles, area, perimeter, etc.

    private Side sideAB;
    private Side sideBC;
    private Side sideAC;

    private Angle angleA = new Angle();
    private Angle angleB = new Angle();
    private Angle angleC = new Angle();

    public Triangle(double x1, double y1, double x2, double y2, double x3, double y3)
    {

        a = new Coordinate(x1, y1);
        b = new Coordinate(x2, y2);
        c = new Coordinate(x3, y3);

        sideAB= new Side(a, b);
        sideBC= new Side(b, c);
        sideAC= new Side(a, c);

        angleA.setAngle(sideAB, sideAC, sideBC);
        angleB.setAngle(sideAB, sideBC, sideAC);
        angleC.setAngle(sideAC, sideBC, sideAB);
    }

    /** Your other methods to calculate Perimeter and Area **/
}

I have just shown the constructor showing how you need to set all the three attributes of triangle class..

I think this much information will help you move forward to create a good design..

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM