AM This website is entirely devoted to the 1500 or more special points now known about triangles: http://www.xtec.es/~qcastell/ttw/ttweng/portada.html
##+########################################################################## # # Triangle Madness -- Shows some interesting properties of a triangle # by Keith Vetter - July 7, 2003 # January 10, 2007 -- added 9 point circle # package require Tk catch {package require tile} array set P {1 {17 -227} 2 {-215 66} 3 {156 192}} ;# Initial position set S(title) "Triangle Madness" set S(circumcenter) 1 set S(incenter) 1 set S(centroid) 0 set S(orthocenter) 0 set S(eulerline) 0 set S(ninepointcircle) 0 set S(morley'smiracle) 0 set S(ninepointcircle) 0 set C(circumcenter) magenta set C(incenter) deeppink set C(centroid) navy set C(orthocenter) DeepSkyBlue2 set C(eulerline) black set C(9point) blueviolet proc DoDisplay {} { global P S wm title . $S(title) pack [frame .ctrl -relief ridge -bd 2 -padx 5 -pady 5] \ -side right -fill both -ipady 5 button .about -text About -command About .about configure -font "[font actual [.about cget -font]] -weight bold" option add *font [.about cget -font] option add *Checkbutton.anchor w option add *Checkbutton.relief raised foreach txt [list Circumcenter Incenter Centroid Orthocenter \ "Euler Line" "Nine Point Circle" "Morley's Miracle"] { set w [string tolower [string map {" " ""} $txt]] checkbutton .$w -variable S($w) -text $txt -command DrawLines button .h_$w -text "?" -command [list Help $txt $w] grid .$w .h_$w -in .ctrl -sticky news } grid rowconfigure .ctrl 50 -weight 10 grid .about - -in .ctrl -row 100 -sticky ew canvas .c -width 500 -height 500 -bd 2 -relief raised bind all <Alt-c> {console show} bind .c <Configure> {ReCenter %W %h %w} pack .c -side top -fill both -expand 1 foreach w {1 2 3} { .c create oval [Box $P($w)] -tag [list vert p$w] -fill red .c bind p$w <B1-Motion> [list DoButton $w %x %y] } bind all <Alt-c> [list console show] } ##+########################################################################## # # Recenter -- keeps 0,0 at the center of the canvas during resizing # proc ReCenter {W h w} { ;# Called by configure event set h2 [expr {$h / 2}] set w2 [expr {$w / 2}] $W config -scrollregion [list -$w2 -$h2 $w2 $h2] } ##+########################################################################## # # DoButton -- interactively moves a vertex around and redraws everything # proc DoButton {who X Y} { foreach {x y} $::P($who) {X Y} [list [.c canvasx $X] [.c canvasy $Y]] break set ::P($who) [list $X $Y] .c move p$who [expr {$X - $x}] [expr {$Y - $y}] DrawLines } ##+########################################################################## # # Box -- returns coordinates for a box around a given point # proc Box {xy {r 5}} { foreach {x y} $xy break return [list [expr {$x-$r}] [expr {$y-$r}] [expr {$x+$r}] [expr {$y+$r}]] } ##+########################################################################## # # VAdd -- adds two vectors w/ scaling of 2nd vector # VSub -- subtract two vectors # VNormal -- returns normal vector to v # proc VAdd {v1 v2 {scaling 1}} { foreach {x1 y1} $v1 {x2 y2} $v2 break return [list [expr {$x1 + $scaling*$x2}] [expr {$y1 + $scaling*$y2}]] } proc VSub {v1 v2} { return [VAdd $v1 $v2 -1] } proc VNormal {v} { foreach {x y} $v break; return [list $y [expr {-1 * $x}]]} ##+########################################################################## # # DrawLines -- draws all the various lines on the screen # proc DrawLines {} { global P .c delete outer circum incenter centroid ortho euler equi tri .c create poly [concat $P(1) $P(2) $P(3)] -tag outer -width 2 \ -fill lightgreen -outline black set theta [FindAngle3 $P(1) $P(2) $P(3)] ;# Check for collinear points if {$theta != 0} { CircumCenter InCenter Centroid OrthoCenter EulerLine 9PointCircle Morley } .c raise vert } ##+########################################################################## # # IntersectV -- find where 2 point/vector intersect # # p1+K(v1) = p3+J(v3) # convert into and solve matrix equation (a b / c d) ( K / J) = ( e / f ) # proc IntersectV {p1 v1 p3 v3} { foreach {x1 y1} $p1 {vx1 vy1} $v1 {x3 y3} $p3 {vx3 vy3} $v3 break set a $vx1 set b [expr {-1 * $vx3}] set c $vy1 set d [expr {-1 * $vy3}] set e [expr {$x3 - $x1}] set f [expr {$y3 - $y1}] set det [expr {double($a*$d - $b*$c)}] if {$det == 0} {error "Determinant is 0"} set k [expr {($d*$e - $b*$f) / $det}] #set j [expr {($a*$f - $c*$e) / $det}] return [VAdd $p1 $v1 $k] } ##+########################################################################## # # Intersect -- find where two line intersect given two points on each line # proc Intersect {p1 p2 p3 p4} { return [IntersectV $p1 [VSub $p2 $p1] $p3 [VSub $p4 $p3]] } ##+########################################################################## # # TrisectAngle -- returns two points which are on the two lines trisecting # the angle created by points p1,p2,p3. We use the cross product to tell # us clockwise ordering. # proc TrisectAngle {p1 p2 p3} { set cross [Cross [VSub $p2 $p1] [VSub $p2 $p3]] if {$cross < 0} {foreach {p1 p3} [list $p3 $p1] break} set theta [FindAngle3 $p1 $p2 $p3] ;# What the angle is set theta1 [expr {$theta / 3.0}] ;# 1/3 of that angle set theta2 [expr {2 * $theta1}] ;# 2/3 of that angle set v [VSub $p3 $p2] ;# We'll rotate this leg set v1 [RotateCC $v $theta1] ;# By 1/3 set v2 [RotateCC $v $theta2] ;# By 2/3 set t1 [VAdd $p2 $v1] ;# Trisect point 1 set t2 [VAdd $p2 $v2] ;# Trisect point 2 if {$cross < 0} { foreach {t1 t2} [list $t2 $t1] break } return [list $t1 $t2] } ##+########################################################################## # # BisectAngle -- returns point on bisector of an angle # proc BisectAngle {p1 p2 p3} { foreach {x1 y1} [VSub $p1 $p2] {x2 y2} [VSub $p3 $p2] break set s1 [expr {100.0 / hypot($x1, $y1)}] set s2 [expr {100.0 / hypot($x2, $y2)}] set v1 [VAdd $p2 [list $x1 $y1] $s1] ;# Unit vector from p2 to p1 set v2 [VAdd $p2 [list $x2 $y2] $s2] ;# Unit vector from p2 to p3 return [VAdd $v1 [VSub $v2 $v1] .5] } ##+########################################################################## # # FindAngle3 -- returns the angle between three points # proc FindAngle3 {p1 p2 p3} { foreach {x1 y1} [VSub $p1 $p2] {x2 y2} [VSub $p3 $p2] break set m1 [expr {hypot($x1,$y1)}] set m2 [expr {hypot($x2,$y2)}] if {$m1 == 0 || $m2 == 0} { return 0 } ;# Coincidental points set dot [expr {$x1 * $x2 + $y1 * $y2}] set theta [expr {acos($dot / $m1 / $m2)}] if {$theta < 1e-5} {set theta 0} return $theta } ##+########################################################################## # # RotateCC -- rotates vector v by beta radians counter-clockwise # proc RotateCC {v beta} { foreach {x y} $v break set xx [expr {$x * cos(-$beta) - $y * sin(-$beta)}] set yy [expr {$x * sin(-$beta) + $y * cos(-$beta)}] return [list $xx $yy] } ##+########################################################################## # # Cross -- returns the cross product -- easy w/ z=0 # proc Cross {v1 v2} { foreach {x1 y1} $v1 {x2 y2} $v2 break return [expr {($x1*$y2) - ($y1*$x2)}] } proc About {} { set msg "$::S(title)\nby Keith Vetter, July 2003\n\n" append msg "A little program to visualize some of the many interesting\n" append msg "properties of a triangle. You can grab and drag any of the\n" append msg "triangle's vertices. Click on the \"?\" next to a property\n" append msg "to learn more about it." tk_messageBox -title "About $::S(title)" -message $msg } proc CircumCenter {} { global P S C .c delete circum set h12 [VAdd $P(1) [VSub $P(2) $P(1)] .5] ;# Midpoints of each side set h13 [VAdd $P(1) [VSub $P(3) $P(1)] .5] set h23 [VAdd $P(2) [VSub $P(3) $P(2)] .5] set n12 [VNormal [VSub $P(2) $P(1)]] ;# Normal to side p1-p2 set n13 [VNormal [VSub $P(3) $P(1)]] set O [IntersectV $h12 $n12 $h13 $n13] ;# The circumcenter set S(v,circumcenter) $O foreach {rx ry} [VSub $P(1) $O] break ;# Radius vector set radius [expr {hypot($rx,$ry)}] ;# Radius magnitude if {! $S(circumcenter) && ! $S(eulerline)} return .c create text $O -text "O" -anchor se -tag circum -fill $C(circumcenter) .c create oval [Box $O 3] -tag circum -fill $C(circumcenter) -outline $C(circumcenter) if {! $S(circumcenter)} return .c create line [concat $h12 $O] -tag circum -fill $C(circumcenter) .c create line [concat $h13 $O] -tag circum -fill $C(circumcenter) .c create line [concat $h23 $O] -tag circum -fill $C(circumcenter) .c create oval [Box $O $radius] -tag circum -outline $C(circumcenter) .c create line [concat $O [VAdd $O [list $radius 0]]] -tag circum \ -fill $C(circumcenter) -dash 1 } proc InCenter {} { global P S C .c delete incenter set b1 [BisectAngle $P(3) $P(1) $P(2)] ;# Bisect angle 1 set b2 [BisectAngle $P(1) $P(2) $P(3)] ;# Bisect angle 2 set Q [Intersect $P(1) $b1 $P(2) $b2] set S(v,incenter) $Q # Need distance from any side to the incenter foreach {qx qy p1x p1y p2x p2y} [concat $Q $P(1) $P(2)] break set vqx [expr {$qx - $p1x}] set vqy [expr {$qy - $p1y}] set vpx [expr {$p2x - $p1x}] set vpy [expr {$p2y - $p1y}] set radius [expr {($vqx * $vpy - $vqy * $vpx) / hypot($vpx, $vpy)}] if {! $S(incenter)} return .c create text $Q -text "Q" -anchor se -tag incenter -fill $C(incenter) .c create oval [Box $Q 3] -tag incenter -fill $C(incenter) -outline $C(incenter) .c create line [concat $P(1) $Q] -tag incenter -fill $C(incenter) .c create line [concat $P(2) $Q] -tag incenter -fill $C(incenter) .c create line [concat $P(3) $Q] -tag incenter -fill $C(incenter) .c create oval [Box $Q $radius] -tag incenter -outline $C(incenter) .c create line [concat $Q [expr {$qx + $radius}] $qy] -tag incenter \ -fill $C(incenter) -dash 1 } proc Centroid {} { global P S C .c delete centroid set h12 [VAdd $P(1) [VSub $P(2) $P(1)] .5] ;# Midpoints of each side set h13 [VAdd $P(1) [VSub $P(3) $P(1)] .5] set h23 [VAdd $P(2) [VSub $P(3) $P(2)] .5] set CC [Intersect $P(1) $h23 $P(2) $h13] set S(v,centroid) $CC if {! $S(centroid) && ! $S(eulerline)} return set col $C(centroid) .c create text $CC -text "C" -anchor se -tag centroid -fill $col .c create oval [Box $CC 3] -tag centroid -fill $col -outline $col if {! $S(centroid)} return .c create line [concat $P(1) $h23] -tag centroid -fill $col .c create line [concat $P(2) $h13] -tag centroid -fill $col .c create line [concat $P(3) $h12] -tag centroid -fill $col } proc OrthoCenter {} { global P S C .c delete ortho set v1 [VSub $P(3) $P(2)] ;# Vector for side p2-p3 set a1 [IntersectV $P(1) [VNormal $v1] $P(2) $v1] set v2 [VSub $P(3) $P(1)] set a2 [IntersectV $P(2) [VNormal $v2] $P(1) $v2] set v3 [VSub $P(2) $P(1)] set a3 [IntersectV $P(3) [VNormal $v3] $P(1) $v3] set H [Intersect $P(1) $a1 $P(2) $a2] set S(v,orthocenter) $H if {! $S(orthocenter) && ! $S(eulerline)} return set col $C(orthocenter) .c create text $H -text "H" -anchor se -tag ortho -fill $col .c create oval [Box $H 3] -tag ortho -fill $col -outline $col if {! $S(orthocenter)} return .c create line [concat $P(1) $a1] -tag ortho -fill $col .c create line [concat $P(2) $a2] -tag ortho -fill $col .c create line [concat $P(3) $a3] -tag ortho -fill $col # Altitude's feet maybe outside triangle, draw a dashed line to look bettter .c create line [concat $P(2) $P(3) $a1] -tag {x ortho} -fill $col -dash 1 .c create line [concat $P(1) $P(3) $a2] -tag {x ortho} -fill $col -dash 1 .c create line [concat $P(1) $P(2) $a3] -tag {x ortho} -fill $col -dash 1 .c lower x } proc EulerLine {} { global P S C if {! $S(eulerline)} return .c delete euler .c create line [concat $S(v,orthocenter) $S(v,centroid)] -tag euler .c create line [concat $S(v,circumcenter) $S(v,centroid)] -tag euler .c itemconfig euler -fill $C(eulerline) -width 3 } proc 9PointCircle {} { global P S C .c delete 9point if {! $S(ninepointcircle)} return # 3 side medians set h12 [VAdd $P(1) [VSub $P(2) $P(1)] .5] ;# Midpoints of each side set h13 [VAdd $P(1) [VSub $P(3) $P(1)] .5] set h23 [VAdd $P(2) [VSub $P(3) $P(2)] .5] # 3 feet of altitudes set v1 [VSub $P(3) $P(2)] ;# Vector for side p2-p3 set a1 [IntersectV $P(1) [VNormal $v1] $P(2) $v1] set v2 [VSub $P(3) $P(1)] set a2 [IntersectV $P(2) [VNormal $v2] $P(1) $v2] set v3 [VSub $P(2) $P(1)] set a3 [IntersectV $P(3) [VNormal $v3] $P(1) $v3] set H [Intersect $P(1) $a1 $P(2) $a2] ;# Orthocenter # 3 midpoints of orthocenter to vertices set o1 [VAdd $P(1) [VSub $H $P(1)] .5] set o2 [VAdd $P(2) [VSub $H $P(2)] .5] set o3 [VAdd $P(3) [VSub $H $P(3)] .5] foreach {O radius} [GetCenter $h12 $h13 $h23] break foreach who {h12 h13 h23 a1 a2 a3 o1 o2 o3 O} { set xy [Box [set $who] 3] .c create oval $xy -tag 9point -fill $C(9point) -outline $C(9point) } set xy [Box $O $radius] .c create oval $xy -tag 9point -fill {} -outline $C(9point) .c create line [concat $O [VAdd $O [list $radius 0]]] -tag 9point \ -fill $C(9point) -dash 1 .c create text $O -text "9" -anchor se -tag 9point -fill $C(9point) } proc GetCenter {p1 p2 p3} { set h12 [VAdd $p1 [VSub $p2 $p1] .5] ;# Midpoints of each side set h13 [VAdd $p1 [VSub $p3 $p1] .5] set h23 [VAdd $p2 [VSub $p3 $p2] .5] set n12 [VNormal [VSub $p2 $p1]] ;# Normal to side p1-p2 set n13 [VNormal [VSub $p3 $p1]] set O [IntersectV $h12 $n12 $h13 $n13] ;# The circumcenter set S(v,circumcenter) $O foreach {rx ry} [VSub $p1 $O] break ;# Radius vector set radius [expr {hypot($rx,$ry)}] ;# Radius magnitude return [list $O $radius] } ##+########################################################################## # # Morley -- draws the angle trisectors out to where they # meet and then draws the Morley triangle in the middle. # proc Morley {} { global P .c delete tri equi if {! $::S(morley'smiracle)} return # Get trisector lines out of each vertex foreach {t(1,1) t(1,2)} [TrisectAngle $P(3) $P(1) $P(2)] break foreach {t(2,1) t(2,2)} [TrisectAngle $P(1) $P(2) $P(3)] break foreach {t(3,1) t(3,2)} [TrisectAngle $P(2) $P(3) $P(1)] break # Find where trisector line segments intersect set E1 [Intersect $P(1) $t(1,1) $P(2) $t(2,2)] set E2 [Intersect $P(2) $t(2,1) $P(3) $t(3,2)] set E3 [Intersect $P(1) $t(1,2) $P(3) $t(3,1)] if {$E1 == {} || $E2 == {} || $E3 == {}} return ;# Colinear lines? .c create line [concat $P(1) $E1 $P(2) $E2 $P(3) $E3 $P(1)] -tag tri \ -fill blue .c create line [concat $E1 $E2 $E3 $E1] -tag equi -fill red -width 2 } ##+########################################################################## # # MyHelpBox -- like tk_messageBox but w/o the grab # proc MyHelpBox {title msg} { set W .help if {[winfo exists $W]} { wm title $W $title $W.msg config -text $msg return } destroy $W toplevel $W wm transient $W . wm title $W $title wm resizable $W 0 0 set bg [$W cget -background] frame $W.bot -background $bg pack $W.bot -side bottom -fill both frame $W.top -background $bg pack $W.top -side top -fill both -expand 1 set windowingsystem [tk windowingsystem] if {$windowingsystem ne "classic" && $windowingsystem ne "aqua"} { $W.bot configure -relief raised -bd 1 $W.top configure -relief raised -bd 1 } label $W.icon -image ::img::info -bg $bg label $W.msg -anchor nw -justify left -text $msg -bg $bg -wraplength 3i grid $W.icon $W.msg -in $W.top -sticky news -padx 2m -pady 2m grid config $W.icon -sticky n grid columnconfigure $W.top 1 -weight 1 grid rowconfigure $W.top 0 -weight 1 if {[info commands ::ttk::button] ne {}} { ::ttk::button $W.ok -text "OK" -command [list destroy $W] $W.top config -bd 0 $W.bot config -bd 0 } else { button $W.ok -text "OK" -padx 3m -command [list destroy $W] } grid $W.ok -in $W.bot -padx 3m -pady 2m -sticky ew bind $W <space> [list destroy $W] ::tk::PlaceWindow $W widget . } image create photo ::img::info -data { R0lGODlhIAAgALMAAAAAAAAA/4SEhMbGxvf/Mf////////////////////////////////////// /////yH5BAEAAAQALAAAAAAgACAAAAStkMhJibj41s0nHkUoDljXXaCoqqRgUkK6zqP7CvQQ7IGs AiYcjcejFYAb4ZAYMB4rMaeO51sNkBKlc/uzRbng0NWlnTF3XAAZzExj2ET3BV7cqufctv2Tj0vv Fn11RndkVSt6OYVZRmeDXRoTAGFOhTaSlDOWHACHW2MlHQCdYFebN6OkVqkZlzcXqTKWoS8wGJMh s7WoIoC7v7i+v7uTwsO1o5HHu7TLtcodEQAAOw==} proc Help {title who} { set m(orthocenter) "The orthocenter is the intersection of the " append m(orthocenter) "three altitudes of a triangle." set m(centroid) "The centroid is the balance point of the triangle. " append m(centroid) "It is the intersection of the lines from each vertex " append m(centroid) "to the opposite median." set m(incenter) "The incenter is the center of a circle that is tangent " append m(incenter) "to each side of the triangle. It is the intersection " append m(incenter) "of the lines bisecting each vertex." set m(circumcenter) "The circumcenter is the center of a circle that " append m(circumcenter) "passes through each vertex of a triangle. It is " append m(circumcenter) "the intersection the lines perpendicular to and " append m(circumcenter) "passing through the midpoint of each side." set m(morley'smiracle) "Morley's Miracle is an equilateral triangle formed " append m(morley'smiracle) "by the three points of intersection of the " append m(morley'smiracle) "adjacent trisectors of the angles of a triangle." set m(eulerline) "Euler Line is the lined formed by the collinear points: " append m(eulerline) "centroid, circumcenter, othrocenter and the center " append m(eulerline) "of the nine-point circle." set m(ninepointcircle) "The nine-point circle is also known as both the " append m(ninepointcircle) "Euler Circle and Feuerbach Circle and is a " append m(ninepointcircle) "circle passing through nine seemingly random " append m(ninepointcircle) "points: the midpoints of the three sides, the " append m(ninepointcircle) "feet of the three altitudes, and the midpoints " append m(ninepointcircle) "of the three line segments connecting the " append m(ninepointcircle) "orthocenter to the vertices." set msg "$title\n\n$m($who)" #tk_messageBox -title "$::S(title) -- $title" -message $msg MyHelpBox "$::S(title) -- $title" $msg } ################################################################ DoDisplay DrawLines returnSee also: Circumcenter of three points