Geometria Computacional - Triângulos

João Pedro Neto

O triângulo é outra figura geométrica básica e que possui imensas aplicações.

Existem várias formas de especificar um triângulo. Definir os seus vértices, os seus ângulos ou, sem associação a coordenadas, definir a dimensão dos seus lados.

A classe seguinte opta por ter duas representações mutuamente exclusivas, uma definida pelos seus vértices e outra pela dimensão dos seus lados. Assim, consoante a informação disponível no problema, podemos construir um triângulo adequado. Para tal, haverá um atributo booleano que nos informa qual a representação de um dado objeto:


class Triangle {
  
  Point  A, B, C;   // defined by points
  double a, b, c;   // defined by sizes (no fixed reference) 
  boolean byPoints; // informs which representation we are using
  
  public Triangle(double a, double b, double c) {
    this.a=a; this.b=b; this.c=c; byPoints=false;
  }

  public Triangle(Point a, Point b, Point c) {
    this.A=a; this.B=b; this.C=c; byPoints=true;
  }
  
  public static double area(double a, double b, double c) { // Heron's formula
    double s = (a+b+c)/2.0;             // semi-perimeter
    return sqrt(s*(s-a)*(s-b)*(s-c));
  }

  // another method: https://www.futilitycloset.com/2018/09/07/neat-3/
  public static double area(Point p1, Point p2, Point p3) {
    return area(p1.distance(p2), p2.distance(p3), p3.distance(p1));
  }

  public double area() { return byPoints ? area(A,B,C) : area(a,b,c);  }

  public static double perimeter(Point p1, Point p2, Point p3) {
    return p1.distance(p2) + p2.distance(p3) + p3.distance(p1);
  }

  public double perimeter() {
    return byPoints ? perimeter(A,B,C) : a+b+c;
  }

O próximo método devolve o círculo inscrito num triângulo:

Este círculo pode ser descoberto pela intersecção de linhas que bisectam os ângulos do triângulo [ref]:


// returns the triangle's incircle
public Circle inCircle() {
    if (!byPoints) return null;
    
    Line angleBisector1 = Line.angleBisector(A, B, C),
         angleBisector2 = Line.angleBisector(B, A, C);
    
    Point center  = angleBisector1.intersect(angleBisector2);
    double radius = Triangle.area(A, B, C) / (0.5*Triangle.perimeter(A, B, C));
    return new Circle(center, radius);
}

Também podemos calcular o círculo que inscreve o triângulo.

Este centro é a intersecção dos bisectores dos lados dos triângulos (o raio possui uma fórmula direta, cf aqui)


// returns the triangle's outcircle
public Circle outCircle() {
    if (!byPoints) return null;
    
    Line b1 = Line.bisector(A, B),
         b2 = Line.bisector(B, C);
    
    Point center  = b1.intersect(b2); 
    double radius = A.distance(B)*B.distance(C)*C.distance(A) / (4*Triangle.area(A, B, C));
    return new Circle(center, radius);
}

De notar que só podemos devolver estes círculos quando trabalhamos com a representação do triângulo por vértices.

Se tivermos só os lados, apenas podemos conhecer o raio destes círculos:


// returns the triangle's incircle radius (if we just know the sizes)
// @pre: !byPoints
public double radiusInCircle() {
    return Triangle.area(a, b, c) / (0.5*(a+b+c));
}

// returns the triangle's outcircle radius (if we just know the sizes)
// @pre: !byPoints
public double radiusOutCircle() {
    return a*b*c / (4*Triangle.area(a, b, c));
}

Outro método útil é saber quais os ângulos do triângulo. Se tivermos apenas os lados, os ângulos podem ser calculados pelas leis dos cosenos.


// return array of the triangle angles in radian
// to translate to degree use: 
//   Arrays.stream(t.angles()).map(rad -> rad*180.0/PI).toArray();
public double[] angles() {
    if (byPoints)
        return new double[] {Point.angle(C,A,B), Point.angle(A,B,C),Point.angle(B,C,A)};
    else {
        double a2=a*a, b2=b*b, c2=c*c; 
        return new double[] {acos((b2+c2-a2)/(2*b*c)),
                             acos((c2+a2-b2)/(2*a*c)),
                             acos((a2+b2-c2)/(2*a*b))};
    }
}

Uma nota sobre o método de cálculo da área. A fórmula de Heron é muito conhecida e matematicamente correta. Porém, no mundo dos double's esta fórmula levanta problemas de aproximação no caso de triângulos muito finos, onde dois lados são muito maiores que outro. Isto porque o semi-perímetro s vai ser muito similar aos dois lados maiores, e a subtracção de dois números muito similares pode levar a perda catastrófica de precisão (cf. o tutorial sobre este assunto).

Nesta situação, deve-se substituir por uma fórmula equivalente mais estável. A seguinte foi criada por William Kahan (devem respeitar os parenteses):

$$A = \frac{1}{4} \sqrt{(a+(b+c))(c-(a-b))(c+(a-b))(a+(b-c))}$$

Problemas UVas sugeridos:

Last modified: Friday, 28 February 2020, 8:51 AM