Una possibile soluzione, molto più esauriente rispetto alle richieste stesse del problema è la seguente (è stata “ragionevolmente” controllata, ma se qualcuno trovasse dei difetti di funzionamento è pregato di segnalarlo) #include <iostream> #include <iomanip> #include <math.h> #define ORTHO 0 #define BARIC 1 #define CIRCO 2 #define INCEN 3 #define PERIMETRO UNITARIO 1 #define AREA UNITARIA 2 using namespace std; /* questa funzione non è membro della classe e serve a convertire i tre angoli di un triangolo da radianti a gradi sessagesimali, primi, secondi */ int ** converti(double a[]) {int ** i; double dumm, domm; i = new int*[3]; for(int j=0; j < 3; j++) i[j] = new int[3], domm = a[j]*180.0/M PI, i[j][0] = static cast<int>(floor(domm)), dumm = (domm - static cast<double>(i[j][0]))*60.0, i[j][1] = static cast<int>(floor(dumm)), domm = (dumm - static cast<double>(i[j][1]))*60.0, i[j][2] = static cast<int>(floor(domm)); if(i[0][2]+i[1][2]+i[2][2]) i[2][2]++; return i;} /* qui inizia la classe dei triangoli */ class Triangolo { /* la variabile booleana "fatto" indica se è stato possibile costruire effettivamente il triangolo*/ bool fatto; // verifica delle disuguaglianze triangolari bool testCongruenzaLati(double l[], char q) {bool r = true; for(char c=0; c < q; c++) r = r && l[c] > 0.0; if(q == 2) return r; return r && l[0] < l[1]+l[2] && l[1] < l[2]+l[0] && l[2] < l[0]+l[1] && l[0] >= fabs(l[1]-l[2]) && l[1] >= fabs(l[2]-l[0]) && l[2] >= fabs(l[0]-l[1]);} /* ordinamento dei lati dal maggiore al minore e "riconoscimento" del grado di "regolarità" del triangolo */ void ordinaLati() {if(fabs(lati[0] - lati[1]) > 1.e-8*lati[0] && fabs(lati[0] - lati[2]) > 1.e-8*lati[0] && fabs(lati[1] - lati[2]) > 1.e-8*lati[1]) regolarita = scaleno; else if(fabs(lati[0] - lati[1]) <= 1.e-8*lati[0] || fabs(lati[0] - lati[2]) <= 1.e-8*lati[0]) {if(fabs(lati[1] - lati[2]) <= 1.e-8*lati[1]) lati[2] = lati[1] = lati[0], regolarita = equilatero; else lati[1] = lati[0], regolarita = isoscele;} i[0] = ((lati[0] >= lati[1]) ? ((lati[0] >= lati[2]) ? 0 : 2) : ((lati[1] >= lati[2]) ? 1 : 2)), i[2] = ((lati[0] < lati[1]) ? ((lati[0] < lati[2]) ? 0 : 2) : ((lati[1] < lati[2]) ? 1 : 2)); i[1] = 3 - i[0] - i[2];} /* calcolo del punto d’incontro di due rette */ double * incrociaRette(double * r1, double *r2) {double d; if((d = r1[0]*r2[1] - r1[1]*r2[0]) == 0.0) return 0; double *xy = new double[2]; xy[0] = (r1[1]*r2[2]-r1[2]*r2[1]) / d, xy[1] = (r1[2]*r2[0]-r1[0]*r2[2]) / d; return xy;} /* calcolo dei coefficienti dell’equazione della retta bisettrice dell’angolo numero a */ double * rettaBisettrice(int a) {double * c = new double[3]; c[1] = 1.0; switch(a) {case 0: c[0] = tan(angoli[2]+0.5*angoli[a]), c[2] = -vertici[2][1] -vertici[2][0]*c[0]; break; case 1: c[0] = -tan(0.5*angoli[a]), c[2] = 0.0; break; case 2: c[0] = tan(0.5*angoli[a]), c[2] = lati[0]*c[0];} return c;} /* calcolo dei coefficienti dell’equazione della retta che porta l’altezza relativa al lato numero a */ double * rettaAltezza(int a) {double * c = new double[3]; switch(a) {case 0: c[1] = 0.0, c[0] = 1.0, c[2] = -vertici[2][0]; break; case 1: if(rette[1][1] == 0.0) c[0] = c[2] = 0.0, c[1] = 1.0; else c[0] = -rette[1][1]/rette[1][0], c[1] = 1.0, c[2] = 0.0; break; case 2: if(rette[2][1] == 0.0) c[0] = c[2] = 0.0, c[1] = 1.0; else c[0] = -rette[2][1]/rette[2][0], c[1] = 1.0, c[2] = 0.0;} return c;} /* calcolo dei coefficienti dell’equazione della retta che porta l’asse del lato numero a */ double * rettaAsse(int a) {double * c = new double[3]; switch(a) {case 0: c[0] = 1.0, c[1] = 0.0, c[2] = -0.5*lati[0]; break; case 1: if(rette[1][1] == 0.0) c[0] = 0.0, c[1] = 1.0, c[2] = -0.5*lati[1]; else c[0] = -rette[1][1]/rette[1][0], c[1] = 1.0, c[2] = 0.5*lati[1]*(c[0]*cos(angoli[2])-sin(angoli[2])) -c[0]*lati[0]; break; case 2: if(rette[2][1] == 0.0) c[0] = 0.0, c[1] = 1.0, c[2] = -0.5*lati[2]; else c[0] = -rette[2][1]/rette[2][0], c[1] = 1.0, c[2] = -0.5*lati[2]*(c[0]*cos(angoli[1])+sin(angoli[1]));} return c;} /* calcolo dei coefficienti dell’equazione della retta che porta la mediana relativa al lato numero a */ double * rettaMediana(int a) {double * c = new double[3]; switch(a) {case 0: if(vertici[2][0] == 0.5*lati[0]) c[0] = 1.0, c[1] = 0.0, c[2] = -vertici[2][0]; else c[0] = 1.0/(0.5*lati[0] - vertici[2][0]), c[1] = 1.0/vertici[2][1], c[2] = -1.0 - vertici[2][0]/(0.5*lati[0] - vertici[2][0]); break; case 1: c[2] = 0.0; if(vertici[2][0] == -lati[0]) c[0] = 1.0, c[1] = 0.0; else c[0] = 2.0/(vertici[2][0]+lati[0]), c[1] = -2.0/vertici[2][1]; break; case 2: if(vertici[2][0] == 2.0*lati[0]) c[0] = 1.0, c[1] = 0.0, c[2] = -lati[0]; else c[0] = 1.0/(0.5*vertici[2][0]-lati[0]), c[1] = -2.0/vertici[2][1], c[2] = -lati[0]/(0.5*vertici[2][0]-lati[0]);} return c;} /* attribuzione delle coordinate ai vertici del triangolo: il primo vertice sta nell’origine, il secondo lungo l’asse orizzontale, il terzo di conseguenza...; sono anche determinate le equazioni delle rette che portano i tre lati.*/ void calcolaVertici() {vertici[0][0] = vertici[0][1] = 0.0, vertici[1][0] = lati[0], vertici[1][1] = 0.0, vertici[2][0] = lati[2] * cos(angoli[1]), vertici[2][1] = lati[2] * sin(angoli[1]), rette[0][0] = rette[0][2] = 0.0, rette[0][1] = 1.0, rette[1][0] = (vertici[2][0] != vertici[1][0]) ? 1.0 /(vertici[2][0] - vertici[1][0]) : 1.0, rette[1][1] = (vertici[2][0] != vertici[1][0]) ? -1.0 /(vertici[2][1] - vertici[1][1]) : 0.0, rette[1][2] = rette[1][0] == 1.0 ? 0.0 : vertici[1][1] / (vertici[2][1] - vertici[1][1]) vertici[1][0] / (vertici[2][0] - vertici[1][0]); rette[2][0] = (vertici[2][0] != vertici[0][0]) ? 1.0 /(vertici[2][0] - vertici[0][0]) : 1.0, rette[2][1] = (vertici[2][0] != vertici[0][0]) ? -1.0 /(vertici[2][1] - vertici[0][1]) : 0.0, rette[2][2] = rette[2][0] == 1.0 ? 0.0 : vertici[0][1] / (vertici[2][1] - vertici[0][1]) vertici[0][0] / (vertici[2][0] - vertici[0][0]);} static static static static char * double enum regolarita {equilatero, isoscele, scaleno}regolarita; enum forma {Equilatero, acutangolo, rettangolo, ottusangolo}forma; const char * descrizioni regolarita[]; const char * descrizioni forma[]; caratteristiche[2], i[3]; lati[3], angoli[3], vertici[3][2], centri[4][2], rette[3][3]; /* "amicizia" con l’operatore << */ friend ostream & operator << (ostream &, Triangolo); /* un metodo per la creazione di un triangolo equilatero */ void faiEquilatero(double l) {lati[0] = lati[1] = lati[2] = l, angoli[0] = angoli[1] = angoli[2] = M PI/3.0, regolarita = equilatero, forma = Equilatero, caratteristiche[0] = const cast<char *>(descrizioni regolarita[regolarita]), caratteristiche[1] = const cast<char *>(descrizioni forma[forma]), calcolaVertici(), calcolaCentri();} /* un metodo che calcola tutti i centri di un triangolo */ void calcolaCentri() {double *xy, *r, *s; xy = incrociaRette(r = rettaAltezza(0), s = rettaAltezza(1)), centri[ORTHO][0] = xy[0], centri[ORTHO][1] = xy[1], delete[] r, delete[] s, delete [] xy, xy = incrociaRette(r = rettaMediana(0), s = rettaMediana(1)), centri[BARIC][0] = xy[0], centri[BARIC][1] = xy[1], delete[] r, delete[] s, delete [] xy, xy = incrociaRette(r = rettaAsse(0), s = rettaAsse(1)), centri[CIRCO][0] = xy[0], centri[CIRCO][1] = xy[1], delete[] r, delete[] s, delete [] xy, xy = incrociaRette(r = rettaBisettrice(0), s = rettaBisettrice(1)), centri[INCEN][0] = xy[0], centri[INCEN][1] = xy[1], delete[] r, delete[] s, delete [] xy;} /* un metodo che "riconosce" la forma di un triangolo */ void determinaForma() {if(fabs(2.0*angoli[i[0]]-M PI) < 1.e-9) forma = rettangolo; else if(2.0*angoli[i[0]] > M PI) forma = ottusangolo; else if(regolarita != equilatero) forma = acutangolo; else forma = Equilatero;} public: /* metodi PUBBLICI (significato ovvio)*/ bool malRiuscito() {return !fatto;} double * Lati() {return lati;} double Lati(int i) {return lati[i];} double * Angoli() {return angoli;} double Angoli(int i) {return angoli[i];} double Perimetro() {return lati[0]+lati[1]+lati[2];} double Area() {double p = Perimetro(); return 0.25*sqrt(p*(p-2.0*lati[0])*(p-2.0*lati[1])*(p-2.0*lati[2]));} /* costruttore default: costruisce un triangolo equilatero di lato 1 */ Triangolo() {faiEquilatero(1.0), fatto = true;} /* costruttore parametrico num. 1: costruisce un triangolo equilatero di lato l */ Triangolo(double l) {faiEquilatero(l), fatto = true;} /* costruttore parametrico num. 2: costruisce un triangolo (se possibile) assegnati i tre lati */ Triangolo(double l[]) {fatto = testCongruenzaLati(l, 3); if(!fatto) {cerr << "questi lati: " << l[0] << ", " << l[1] << ", " << l[2] << " non possono formare un triangolo\n"; return;} lati[0] = l[0], lati[1] = l[1], lati[2] = l[2], ordinaLati(), angoli[i[0]] = acos((l[i[1]]*l[i[1]]+l[i[2]]*l[i[2]] -l[i[0]]*l[i[0]])/(2.0*l[i[1]]*l[i[2]])); determinaForma(), angoli[i[1]] = asin(sin(angoli[i[0]])*l[i[1]]/l[i[0]]), angoli[i[2]] = asin(sin(angoli[i[0]])*l[i[2]]/l[i[0]]), caratteristiche[0] = const cast<char *>(descrizioni regolarita[regolarita]), caratteristiche[1] = const cast<char *>(descrizioni forma[forma]), calcolaVertici(), calcolaCentri();} /* costruttore parametrico num. 3: costruisce un triangolo assegnati due lati e l’angolo compreso (sempre possibile... salvo patologie) */ Triangolo(double l[], double a) {fatto = testCongruenzaLati(l, 2); if(!fatto) {cerr << "questi lati: " << l[0] << ", " << l[1] << " non possono formare un triangolo\n"; return;} if(!(fatto && a > 0.0 && a < M PI)) {fatto = false; cerr << "quest’angolo: " << a << " non può stare in un triangolo\n"; return;} lati[0] = l[0], lati[1] = l[1], lati[2] = sqrt(l[0]*l[0]+l[1]*l[1]-2.0*l[0]*l[1]*cos(a)), ordinaLati(), angoli[2] = a, angoli[0] = asin(sin(angoli[2])*lati[0]/lati[2]), angoli[1] = asin(sin(angoli[2])*lati[1]/lati[2]), determinaForma(), caratteristiche[0] = const cast<char *>(descrizioni regolarita[regolarita]), caratteristiche[1] = const cast<char *>(descrizioni forma[forma]), calcolaVertici(), calcolaCentri();} /* costruttore parametrico num. 4: costruisce un triangolo assegnato un lato e i due angoli adiacenti */ Triangolo(double l, double a[]) {fatto = a[0] > 0.0 && a[1] > 0.0 && a[0] + a[1] < M PI; if(!fatto) {cout<< "\n...\n\" O cara piota mia che sı̀ t’insusi\n\ che, come veggion le terrene menti\n\ non càpere in trı̈angol due ottusi,\n\n\ cosı̀ vedi le cose contingenti\n\ anzi che sieno in sé, mirando il punto\n\ a cui tutti li tempi son presenti\n...\"\n"; return;} if(!(fatto && l > 0)) {fatto = false; cerr << "questo lato: " << l << " non può appartenere a un triangolo\n"; return;} angoli[1] = a[0], angoli[2] = a[1], angoli[0] = M PI - a[0] - a[1], lati[0] = l, lati[1] = l * sin(angoli[1]) / sin(angoli[0]), lati[2] = l * sin(angoli[2]) / sin(angoli[0]), ordinaLati(), determinaForma(), caratteristiche[0] = const cast<char *>(descrizioni regolarita[regolarita]), caratteristiche[1] = const cast<char *>(descrizioni forma[forma]), calcolaVertici(), calcolaCentri();} /* costruttore parametrico num. 5: costruisce un triangolo assegnati due lati, un angolo e un criterio di scelta nel caso esistano due triangoli compatibili (triangolo NON rettangolo) */ Triangolo(double l[], double a, char c) {fatto = a > 0.0 && a < M PI && l[0] > 0. && l[1] >= l[0] * sin(a); if(!fatto) {cerr << "la configurazione fornita (lati: " << l[0] <<", " << l[1] << "); angolo = " << a << " non può sussistere\n"; return;} angoli[2] = a; if(l[1] == l[0] * sin(a)) angoli[1] = 0.5*M PI, forma = rettangolo; else {angoli[1] = asin(sin(a)*l[0]/l[1]), forma = acutangolo; if(c == ’O’) forma = ottusangolo, angoli[1] = M PI - angoli[1];} angoli[0] = M PI - angoli[1] - angoli[2], lati[1] = l[0], lati[2] = l[1], lati[0] = lati[1] * sin(angoli[0]) / sin(angoli[1]), ordinaLati(), caratteristiche[0] = const cast<char *>(descrizioni regolarita[regolarita]), caratteristiche[1] = const cast<char *>(descrizioni forma[forma]), calcolaVertici(), calcolaCentri();} /* costruttore parametrico num. 6: costruisce un triangolo assegnati i tre angoli e un criterio di scelta tra gli infiniti triangoli della data forma */ Triangolo(double a[], char c) {double p = 1.0; fatto = a[0] > 0.0 && a[1] > 0.0 && a[2] > 0.0 && fabs(a[0]+a[1]+a[2] - M PI) < 1.e-12; if(!fatto) {cerr << "gli angoli assegnati: " << a[0] << ’ ’ << a[1] << ’ ’ << a[2] << " (la cui somma vale " << a[0]+a[1]+a[2] <<") non possono appartenere a un triangolo\n"; return;} angoli[0] = a[0], angoli[1] = a[1], angoli[2] = a[2], determinaForma(), lati[0] = 1.0, lati[1] = sin(angoli[1])/sin(angoli[0]), lati[2] = sin(angoli[2])/sin(angoli[0]), ordinaLati(); switch(c) {case PERIMETRO UNITARIO: p = Perimetro(), lati[0] /= p, lati[1] /= p, lati[2] /= p; break; case AREA UNITARIA: p = sqrt(Area()), lati[0] /= p, lati[1] /= p, lati[2] /= p;} caratteristiche[0] = const cast<char *>(descrizioni regolarita[regolarita]), caratteristiche[1] = const cast<char *>(descrizioni forma[forma]), calcolaVertici(), calcolaCentri();} /* overload dell’operator []: restituisce uno dei centri di un triangolo, secondo il valore trasferito come argomento e dichiarato in direttive define */ double * operator[](char c) {double * r; switch(c) {case ORTHO: case BARIC: case CIRCO: case INCEN: r = centri[c]; break; default: r = 0;} return r;}}; /* FINE DELLA CLASSE PER I TRIANGOLI */ /* variabili statiche */ enum Triangolo :: regolarita Triangolo :: regolarita; enum Triangolo :: forma Triangolo :: forma; const char * Triangolo :: descrizioni regolarita[] = {" equilatero ", " isoscele ", " scaleno "}; const char * Triangolo :: descrizioni forma[] = {" ", " acutangolo ", " rettangolo ", " ottusangolo "}; /* friend operator di output: modificabile a piacimento */ ostream & operator << (ostream & stream, Triangolo t) { int ** gradi minuti secondi = converti(t.angoli); char l; stream << setiosflags(ios::fixed) << setprecision(3) << "informazioni relative a un triangolo" << t.caratteristiche[0] << ’\b’ << t.caratteristiche[1] << "\b:\n"; switch(t.regolarita) {case Triangolo :: equilatero: stream << "lato = " << t.lati[0]<<endl, stream << "angolo = 60 0 0\n"; break; case Triangolo :: isoscele: switch(t.forma) { case Triangolo :: ottusangolo: case Triangolo :: acutangolo: l = 2 * (t.forma == Triangolo :: acutangolo), stream << "base = " << t.lati[t.i[l]] << endl, stream << "lato obliquo = " << t.lati[t.i[2-l]] << endl, stream << "angolo al vertice = " << gradi minuti secondi[t.i[l]][0] << ’ ’ << gradi minuti secondi[t.i[l]][1] << ’ ’ << gradi minuti secondi[t.i[l]][2]<< endl, stream << "angolo alla base = " << gradi minuti secondi[t.i[2-l]][0] << ’ ’ << gradi minuti secondi[t.i[2-l]][1] << ’ ’ << gradi minuti secondi[t.i[2-l]][2]<< endl; break; case Triangolo :: rettangolo: stream << "ipotenusa = " << t.lati[t.i[0]] << endl, stream << "cateto = " << t.lati[t.i[1]] << endl, stream << "angolo acuto = 45 0 0\n";} break; case Triangolo :: scaleno: switch(t.forma) { case Triangolo :: ottusangolo: case Triangolo :: acutangolo: stream << "lati: " << t.lati[0] << ’ ’ << t.lati[1] << ’ ’ << t.lati[2] << endl, stream << "angoli:" << gradi minuti secondi[t.i[0]][0] << ’ ’ << gradi minuti secondi[t.i[0]][1] << ’ ’ << gradi minuti secondi[t.i[0]][2] << "; " << gradi minuti secondi[t.i[1]][0] << ’ ’ << gradi minuti secondi[t.i[1]][1] << ’ ’ << gradi minuti secondi[t.i[1]][2] << "; " << gradi minuti secondi[t.i[2]][0] << ’ ’ << gradi minuti secondi[t.i[2]][1] << ’ ’ << gradi minuti secondi[t.i[2]][2] << ’\n’; break; case Triangolo :: rettangolo: stream << "ipotenusa = " << t.lati[t.i[0]] << endl, stream << "cateti: " << t.lati[t.i[1]] << ’ ’ << t.lati[t.i[2]] << endl, stream << "angoli acuti:" << gradi minuti secondi[t.i[1]][0] << ’ ’ << gradi minuti secondi[t.i[1]][1] << ’ ’ << gradi minuti secondi[t.i[1]][2] << "; " << gradi minuti secondi[t.i[2]][0] << ’ ’ << gradi minuti secondi[t.i[2]][1] << ’ ’ << gradi minuti secondi[t.i[2]][2] << ’\n’;}} stream << "il perimetro del triangolo vale " << t.Perimetro()<<"\ne l’area vale " << t.Area() << endl; return stream;}