// a program to test whether the "Euler line" holds in hyperbolic/spherical geometry // compile with: mymake -O3 rogueviz/tricenter.cpp // run with e.g. // ./hyper -canvas-random 0 -canvas i -geo 0 -tricenter -noplayer -noscr -zoom .95 -run -shot-1000 -shott 1 -fillmodel 000000 -pngshot euler-fail.png // press 'o' to change options #include "rogueviz.h" namespace hr { namespace tricenter { struct simplifier { transmatrix T; vector hs; simplifier(transmatrix _T, vector _hs, vector extra = {}) { T = _T; hs = _hs; for(auto h: hs) (*h) = T * (*h); for(auto h: extra) hs.push_back(h); } ~simplifier() { for(auto h: hs) (*h) = iso_inverse(T) * (*h); } }; bool is_on_line(hyperpoint A, hyperpoint B, hyperpoint X) { simplifier s1(gpushxto0(A), {&A, &B, &X}); simplifier s2(spintox(B), {&A, &B, &X}); return abs(X[1]) < 1e-6; } hyperpoint perpendicular_drop(hyperpoint A, hyperpoint B, hyperpoint X) { if(1) { simplifier s1(gpushxto0(A), {&A, &B, &X}); simplifier s2(spintox(B), {&A, &B, &X}); X[1] = 0; X = normalize(X); } return X; } hyperpoint d1(hyperpoint A, hyperpoint B, ld d = 1.5) { hyperpoint X; if(1) { simplifier s1(gpushxto0(A), {&A, &B}, {&X}); simplifier s2(spintox(B), {&A, &B}, {&X}); X = xpush0(d); } return X; } hyperpoint bisector(hyperpoint X, hyperpoint A, hyperpoint B) { hyperpoint A1 = d1(X, A); hyperpoint B1 = d1(X, B); return mid(A1, B1); } hyperpoint ortho1(hyperpoint A, hyperpoint B) { hyperpoint X; if(1) { simplifier s1(gpushxto0(A), {&A, &B}, {&X}); simplifier s2(spintox(B), {&A, &B}, {&X}); X = ypush0(1.5); } return X; } hyperpoint mirror_line_point(hyperpoint A, hyperpoint B, hyperpoint X) { if(1) { simplifier s1(gpushxto0(A), {&A, &B, &X}); simplifier s2(spintox(B), {&A, &B, &X}); X[1] = -X[1]; } return X; } hyperpoint linecross3(hyperpoint A, hyperpoint A1, hyperpoint B, hyperpoint B1, hyperpoint C, hyperpoint C1, const string& s) { hyperpoint res = linecross(A, A1, B, B1); println(hlog, s, is_on_line(C, C1, res) ? " verification: OK" : " verification: failed"); return res; } double a, b, c; bool show_triangle = true, show_incenter, show_centroid, show_circumcenter, show_orthocenter, show_euler_line, show_ninepoint, show_symmedian, show_gergonne, show_nagel; void tricenter() { hyperpoint A = xspinpush0(a, 1); hyperpoint B = xspinpush0(b, 1); hyperpoint C = xspinpush0(c, 1); shiftmatrix S = ggmatrix(cwt.at); auto markpoint = [&] (hyperpoint h, color_t c) { for(int i=0; i<=360; i++) curvepoint(xspinpush0(i*1._deg, 0.02)); queuecurve(S * rgpushxto0(h), (c << 8) | 0xFF, (c << 8) | 0xC0, PPR::LINE); }; auto markseg = [&] (hyperpoint h1, hyperpoint h2, color_t c) { vid.linewidth *= 3; queueline(S * h1, S * h2, c, 2); vid.linewidth /= 3; }; if(show_triangle) { markpoint(A, 0xFF0000); markpoint(B, 0xFF0000); markpoint(C, 0xFF0000); markseg(A, B, 0xFF0000FF); markseg(B, C, 0xFF0000FF); markseg(C, A, 0xFF0000FF); } // indices as in: https://faculty.evansville.edu/ck6/encyclopedia/ETC.html hyperpoint A1 = bisector(A, B, C); hyperpoint B1 = bisector(B, C, A); hyperpoint C1 = bisector(C, A, B); hyperpoint D1 = linecross3(A, A1, B, B1, C, C1, "incenter"); ignore(D1); if(show_incenter) { markseg(A, A1, 0x00FF00FF); markseg(B, B1, 0x00FF00FF); markseg(C, C1, 0x00FF00FF); markpoint(D1, 0x00FF00); } hyperpoint C2 = mid(A, B); hyperpoint A2 = mid(B, C); hyperpoint B2 = mid(C, A); hyperpoint D2 = linecross3(A2, A, B2, B, C2, C, "centroid"); if(show_centroid) { markseg(A2, A, 0xFF00FFFF); markseg(B2, B, 0xFF00FFFF); markseg(C2, C, 0xFF00FFFF); markpoint(D2, 0xFF00FF); } hyperpoint C3 = ortho1(C2, A); hyperpoint A3 = ortho1(A2, B); hyperpoint B3 = ortho1(B2, C); hyperpoint D3 = linecross3(A2, A3, B2, B3, C2, C3, "circumcenter"); if(show_circumcenter) { markseg(A2, A3, 0x00FFFFFF); markseg(B2, B3, 0x00FFFFFF); markseg(C2, C3, 0x00FFFFFF); markpoint(D3, 0x00FFFF); } hyperpoint C4 = perpendicular_drop(A, B, C); hyperpoint A4 = perpendicular_drop(B, C, A); hyperpoint B4 = perpendicular_drop(C, A, B); hyperpoint D4 = linecross3(A, A4, B, B4, C, C4, "orthocenter"); if(show_orthocenter) { markseg(A, A4, 0xFFFF00FF); markseg(B, B4, 0xFFFF00FF); markseg(C, C4, 0xFFFF00FF); markpoint(D4, 0xFFFF00); } auto markcc = [&] (hyperpoint h1, hyperpoint h2, hyperpoint h3, color_t col, const string& s) { if(is_on_line(h1, h2, h3)) { println(hlog, s, ": lie on a line"); if(h1[0] > h2[0]) swap(h1, h2); if(h2[0] > h3[0]) swap(h3, h2); if(h1[0] > h2[0]) swap(h1, h2); markseg(h1, h3, col); } else { hyperpoint ctr = circumscribe(h1, h2, h3); ld rad = hdist(h1, ctr); for(int i=0; i<=360; i++) curvepoint(xspinpush0(i*1._deg, rad)); queuecurve(S * rgpushxto0(ctr), col, 0, PPR::LINE); } }; if(show_euler_line) markcc(D2, D3, D4, 0xFFFFFFFF, "Euler line"); // the nine-point circle center: // - passes through the centers of the sides hyperpoint D5_a = circumscribe(A2, B2, C2); // - passes through the feet of the altitudes hyperpoint D5_b = circumscribe(A4, B4, C4); // - passes through the midpoints of AD4, BD4 and CD4 hyperpoint D5_c = circumscribe(mid(A, D4), mid(B, D4), mid(C, D4)); if(show_ninepoint) { markpoint(D5_a, 0xC0C0C0); markpoint(D5_b, 0xC0C0C0); markpoint(D5_c, 0xC0C0C0); } vector all = { A2, B2, C2, A4, B4, C4, mid(A, D4), mid(B, D4), mid(C, D4) }; hyperpoint eucenter = circumscribe(D2, D3, D4); ld rad = hdist(eucenter, D2); int score = 0, outof = 0; for(int a=0; a<9; a++) for(int b=0; b<9; b++) for(int c=0; c<9; c++) if(a& v) { if(s != "mixed") return; using namespace tour; v.push_back(slide{ "triangle centers", 10, LEGAL::NONE | QUICKGEO, "An exploration of various 'triangle centers' in non-Euclidean geometry. Press 'o'." , [] (presmode mode) { if(mode == pmStart) enable(); }}); }); } }