import java.*; import java.awt.*; public class Proof2 extends java.applet.Applet { Thread nineptdemo; Button continuebutton, restartbutton; Button ah2ombutton, hppkbutton; int state; Proof2Canvas displayer; public void init() { setBackground(Color.lightGray); displayer = new Proof2Canvas(); state = 1; add(displayer); continuebutton=new Button("Continue"); restartbutton=new Button("Restart"); add(continuebutton); add(restartbutton); ah2ombutton = new Button("AH=2OM"); add(ah2ombutton); hppkbutton = new Button("HP=PK"); add(hppkbutton); repaint(); } // state index: // state 1 - present drawing of triangle // state 2 - LNEF is a rectangle // state 3 - L,M,N,D,E,F all on circle // state 4 - P,Q,R on circle // state 5 - All done, show the circle // state 21 - explain AH = 2 OL // state 41 - explain HP=PK public void addstate2buttons(){ ah2ombutton.show(); } public void removestate2buttons(){ ah2ombutton.hide(); } public void addstate4buttons(){ hppkbutton.show(); } public void removestate4buttons(){ hppkbutton.hide(); } public boolean action(Event ev, Object arg){ if(ev.target instanceof Button && state > 0){ if("Continue".equals(arg)){ switch(state){ case 1: addstate2buttons(); state++; break; case 2: removestate2buttons(); state = 3; break; case 21: addstate2buttons(); state = 2; break; case 3: case 41: addstate4buttons(); state = 4; break; case 4: removestate4buttons(); state = 5; break; case 5: continuebutton.hide(); state = 6; break; default: } } if("Restart".equals(arg)){ if(state == 2){ removestate2buttons(); } if(state == 4){ removestate4buttons(); } if(state == 6){ continuebutton.show(); } state = 1; } if("AH=2OM".equals(arg)){ removestate2buttons(); state = 21; } if("HP=PK".equals(arg)){ removestate4buttons(); state = 41; } displayer.setState(state); repaint(); return true; } return false; } public void paint(Graphics g){ if(state == 1){ ah2ombutton.hide(); hppkbutton.hide(); } displayer.repaint(); } } class Proof2Canvas extends Canvas{ int state; Font font; int fsize; DPoint a,b,c; DPoint l,m,n; DPoint p,q,r; DPoint h; DPoint d,e,f; DLine sideab,sidebc,sideca,altap,altbq,altcr; DLine eh, ol, lm, mn, nl,om, on, ah,dh,oh,dm; DLine hp, ak, bk, ok, xp, hk; DPoint center,o,k; double radius, s; public Proof2Canvas(){ resize(550,300); font = new Font("TimesRoman", Font.PLAIN, 12); fsize = 2+font.getSize(); state = 1; a = new DPoint(40.0,20.0,"A"); b = new DPoint(270.0,20.0,"B"); c = new DPoint(120.0,150.0,"C"); sideab = new DLine(a,b); sidebc = new DLine(b,c); sideca = new DLine(c,a); l = sideab.midpoint(); m = sidebc.midpoint(); n = sideca.midpoint(); l.label = "L"; m.label = "M"; n.label = "N"; r = sideab.footalt(c); p = sidebc.footalt(a); q = sideca.footalt(b); p.label = "P"; q.label = "Q"; r.label = "R"; altap = new DLine(a,p); altbq = new DLine(b,q); altcr = new DLine(c,r); h = altap.intersect(altbq); h.label = "H"; d = new DLine(a,h).midpoint(); e = new DLine(b,h).midpoint(); f = new DLine(c,h).midpoint(); d.label = "D"; e.label = "E"; f.label = "F"; center = l.circumcenter(m,n); center.label = "X"; o = a.circumcenter(b,c); o.label = "O"; radius = center.dist(l); s = o.dist(a); eh = new DLine(e,h); om = new DLine(o,m); lm = new DLine(l,m); mn = new DLine(m,n); nl = new DLine(n,l); ol = new DLine(o,l); on = new DLine(o,n); ah = new DLine(a,h); dh = new DLine(d,h); oh = new DLine(o,h); dm = new DLine(d,m); hp = new DLine(h,p); // Read the proof to see why this next thing works. k = hp.lambdapoint(2.0); k.label = "K"; ak = new DLine(a,k); bk = new DLine(b,k); ok = new DLine(o,k); xp = new DLine(center,p); hk = new DLine(h,k); } public void setState(int s){ state = s; } public void draw1(Graphics g) { g.setColor(Color.black); g.setFont(font); sideab.Show(g); sidebc.Show(g); sideca.Show(g); a.Show(g,7); b.Show(g,1); c.Show(g,4); g.setColor(Color.red); g.drawString("L,M,N are the midpoints of the sides.",300,20); l.Show(g,0); m.Show(g,3); n.Show(g,5); g.setColor(Color.green); g.drawString("P,Q,R are the feet of the altitudes.",300, 20+fsize); p.Show(g,3); q.Show(g,5); r.Show(g,0); altap.Show(g); altbq.Show(g); altcr.Show(g); g.setColor(Color.blue); h.Show(g,7); g.drawString("H is the orthocenter.",300,20+2*fsize); g.setColor(Color.cyan); d.Show(g,1); e.Show(g,7); f.Show(g,3); g.drawString("D,E,F are the midpoints of AH, BH and CH.",300,20+3*fsize); g.setColor(Color.magenta); o.Show(g,0); g.drawOval( (int)(o.x-s),(int)(o.y-s),(int)(2.0*s),(int)(2.0*s)); g.drawString("O is the circumcenter. The circumcircle is shown.",300,20+4*fsize); } public void draw2(Graphics g){ draw1(g); g.setColor(Color.black); g.drawString("We have AH=2OM. Since D bisects AH, we have DH=OM.",10,180); g.setColor(Color.red); ah.Show(g); om.Show(g); } public void draw3(Graphics g){ draw1(g); g.setColor(Color.red); om.Show(g); dh.Show(g); oh.Show(g); dm.Show(g); center.Show(g,4); g.setColor(Color.black); g.drawString("So DH=OM. Also DH || OM because both are perpendicular to BC.",10,180); g.drawString("Therefore the diagonals OH, DM bisect each other at a point X.",10,180+fsize); } public void draw4(Graphics g){ draw1(g); g.setColor(Color.red); oh.Show(g); dm.Show(g); ak.Show(g); k.Show(g,4); center.Show(g,4); g.setColor(Color.black); g.drawString("OH, DM bisect each other at a point X, so XD=XM. Also XP=XD=XM because",10,180); g.drawString("angle DPM is a right angle (AP is an altitude).",10,180+fsize); g.drawString("Extend AP to meet the circumcircle at point K. Then we have HP=PK.",10,180+2*fsize); } public void draw5(Graphics g){ draw1(g); g.setColor(Color.red); center.Show(g,3); k.Show(g,3); ok.Show(g); hk.Show(g); xp.Show(g); oh.Show(g); g.setColor(Color.black); g.drawString("X is the midpoint of HO, and P is the midpoint of HK.",10,180); g.drawString("Therefore, by similar triangles HXP and HOK, XP=OK/2=",10,180+fsize); g.drawString("circumradius/2.",10,180+2*fsize); g.drawString("Now if we do the same thing with Q,N and E that we just",10,180+3*fsize); g.drawString("did with P,M and D, we get the same center X and the same",10,180+4*fsize); g.drawString("radius circumradius/2. And similarly with R,L and F.",10,180+5*fsize); } public void draw6(Graphics g){ draw1(g); g.setColor(Color.magenta); g.drawOval( (int)(center.x-radius),(int)(center.y-radius),(int)(2.0*radius),(int)(2.0*radius)); center.Show(g,1); } public void draw21(Graphics g){ draw1(g); g.setColor(Color.red); ol.Show(g); om.Show(g); on.Show(g); lm.Show(g); mn.Show(g); nl.Show(g); g.setColor(Color.black); g.drawString("The circumcenter is the intersection of",10,180); g.drawString("the perpendicular bisectors of the sides",10,180+fsize); g.drawString("of triangle ABC. That is, it is the intersection",10,180+2*fsize); g.drawString("of the altitudes of triangle LMN - so O is the",10,180+3*fsize); g.drawString("orthocenter of LMN. By similar triangles, AH/OM=AB/MN=2.",10,180+4*fsize); } public void draw41(Graphics g){ draw1(g); g.setColor(Color.red); sideca.Show(g); altbq.Show(g); sidebc.Show(g); ak.Show(g); k.Show(g,4); bk.Show(g); g.setColor(Color.black); g.drawString("Angle CAP = angle CBQ because each is complementary to angle C.",10,180); g.drawString("Angle CAK = angle CBK because both cut off arc CK.",10,180+fsize); g.drawString("Therefore angle CBK = angle CBH.", 10, 180+2*fsize); g.drawString("Therefore triangles CBK and CBH are congruent, so HP=PK.",10,180+3*fsize); } public void paint(Graphics g) { switch (state) { case 1: { draw1(g); break; } case 2: { draw2(g); break; } case 3: { draw3(g); break; } case 4: { draw4(g); break; } case 5: { draw5(g); break; } case 6: { draw6(g); break; } case 21: { draw21(g); break; } case 41: { draw41(g); break; } default: { draw1(g); break; } } } } class DPoint{ double x, y; String label; public DPoint(double first, double second, String name){ x = first; y = second; label = name; } public DPoint(double first, double second){ x = first; y = second; label = ""; } public void Show(Graphics g, int where){ int first = (int) x; int second = (int) y; // where tells us where to place the character // relative to the point --- 0 = north, 1 = northeast, // 2 = east, ..., 7 = northwest. int xoffs[] = {0,3,5,6,-3,-10,-12,-10}; int yoffs[] = {-5,-3,0,10,13,10,0,-3}; int offsetx = xoffs[where]; int offsety = yoffs[where]; g.drawOval(first-1,second-1,2,2); g.drawString(label,first+offsetx,second+offsety); } public double dist(DPoint q){ return Math.sqrt((q.x - x)*(q.x - x) + (q.y - y)*(q.y - y)); } public DPoint orthocenter(DPoint m, DPoint n){ // orthocenter of triangle DLine sidemn = new DLine(m,n); DLine sidelm = new DLine(this,m); DPoint foot1 = sidemn.footalt(this); DPoint foot2 = sidelm.footalt(n); DLine alt1 = new DLine(this, foot1); DLine alt2 = new DLine(n, foot2); return alt1.intersect(alt2); } public DPoint circumcenter(DPoint b, DPoint c){ // circumcenter of triangle formed by the 3 points DPoint l = new DLine(this,b).midpoint(); DPoint m = new DLine(this,c).midpoint(); DPoint n = new DLine(b,c).midpoint(); return l.orthocenter(m,n); } } class DLine{ DPoint p,q; public DLine(DPoint first, DPoint second){ p = first; q = second; } public void Show(Graphics g){ int x1 = (int) p.x; int y1 = (int) p.y; int x2 = (int) q.x; int y2 = (int) q.y; g.drawLine(x1,y1,x2,y2); } public DPoint lambdapoint(double lambda){ // The result is the point a fraction lambda // of the way from point p to point q. For example, // if lambda == 0.5, we get the midpoint. In // vector notation, r = p*(1-lambda) + q*lambda. return(new DPoint(q.x*lambda + p.x*(1.0-lambda), q.y*lambda + p.y*(1.0-lambda))); } public DPoint midpoint(){ return lambdapoint(0.5); } public DPoint intersect(DLine m){ double numerator = (m.q.x-q.x)*(m.q.y-m.p.y) - (m.q.x-m.p.x)*(m.q.y-q.y); double denominator = (p.x-q.x)*(m.q.y-m.p.y) - (m.q.x-m.p.x)*(p.y-q.y); double lambda = 1.0 - numerator / denominator; return( lambdapoint(lambda)); } public DPoint footalt(DPoint c){ // foot of the altitude from point c to this line // determine lambda so that // lambdapoint h on line qp satisfies // dot product ch.pq == 0. double lambda = ( (q.x-p.x)*(q.x-c.x) + (q.y-p.y)*(q.y-c.y) ) / ( (q.x-p.x)*(q.x-p.x) + (q.y-p.y)*(q.y-p.y) ); return lambdapoint(1.0-lambda); } }