#!/usr/bin/wish -f ##+######################################################################## ## ## SCRIPT: tkCircleChord_SubtendingAngleAtCenterIsTwiceAngleAtCircumference.tk ## ## PURPOSE: This Tk script is meant to demonstrate the theorem --- which is ## sometimes called the 'inscribed angle' theorem for short, or the ## 'central angle' theorem --- that says: ## ## Given a chord in a circle, consider two angles subtending the ## chord --- one angle with vertex at the center of the circle and ## the other angle with vertex on the circumference of the circle ## (and on the same side of the chord as the center of the circle). ## Then the 'central angle' is exactly twice the size of the ## 'circumference angle'. ## ## (Note that for a given chord, there is ONE 'central angle' but ## an INFINITE number of 'circumference angles'.) ## ## REFERENCES: ## - The first half of Chapter 6 of the book 'Trigonometric Delights' ## by Eli Maor, 1998, gives a nice presentation of this 'inscribed angle' ## theorem. Maor points out that there are several truths that follow ## rather quickly from this theorem --- including Thales' theorem ## (that every triangle inscribed in a semicircle, with one side on ## the diameter of the semicircle, is a right triangle) and the ## sine and cosine double angle and half angle formulas. ## ## Also see the web page at ## https://en.wikipedia.org/wiki/Inscribed_angle ## #################### ## SOME GUI FEATURES: ## ## This GUI uses a Tk 'canvas' widget to show the circle and ## and a chord inscribed within it. Without loss of generality, ## we can always draw the chord horizontally (parallel to the ## bottom and top sides of the rectangular canvas). ## ## A 'scale' widget on the GUI allows the user to easily draw the ## chord. The scale widget allows the user to specify an angle, A, ## between 0 and 180 degrees. The chord is drawn connecting ## two points on the circle at angles (270 + A) and (270 - A) degrees, ## using the Tk 'create oval' command on the Tk 'canvas' widget. ## ## The Tk 'create line' command can then be used to draw the two ## sides of the 'central angle' --- by drawing the 2 line segments ## that connect the center of the circle to the 2 endpoints of the ## chord. ## ## A second 'scale' widget on the GUI allows the user to easily ## specify the point location (on the circle) of the vertex of the ## 'circumference angle'. ## ## Two 'Color' buttons on the GUI allow the user to bring up an ## external Tk color-selector script to specify a background ## (canvas) color and a color for the lines (the circle, the chord, ## and the angle sides) and text drawn on the canvas. ## ## A 'ReDraw' button on the GUI allows the user to redraw the ## the image if the user uses the 2 'scale' widgets or the 2 'Color' ## button widgets to change the image to be displayed. ## #################### ## SIZING THE CANVAS: ## ## This GUI could use a canvas without scrollbars and allow the ## canvas to be resized if the GUI window is resized by the user ## (by using '-fill both' and '-expand 1' parameters on the 'pack' ## command for the 'canvas' widget). Then the user could use the ## 'ReDraw' button to redraw the geometry to fit within the resized ## canvas. ## ## An alternative is to use a square canvas WITH scrollbars and ## provide a 'scale' widget on the GUI by which the user can precisely ## specify the width (and height) of the canvas, in pixels. The user ## can changed any of the several scale widgets and the colors, and ## then user can use the 'ReDraw' button to redraw the geometry ## within the square, scrollable canvas --- and the square can ## extend beyond the screen size of the viewing device. ## ##+######################################### ## ADDITIONAL ITEMS TO DISPLAY ON THE CANVAS (optional): ## ## Various numeric properties of the 2 angles (and the chord) can be ## displayed as the 'circumference point' is moved along the ## circumference of the circle --- such as ## - the 2 angles ('central' and 'circumference') ## in degrees or radians, ## - the length of the chord (we assume a radius 1.0 of the circle), ## - the lengths of the 2 sides of the 'circumference angle', ## - the area of the triangle created by the chord and the ## 'central angle', ## - the area of the triangle created by the chord and the ## 'circumference angle', ## - etc. ## ## In fact, some extra lines and text could be drawn on the canvas ## to indicate the proof of the 'double' relationship of the ## two angles (the theorem). ## ##+########### ## GUI WIDGETS: ## ## This script provides a Tk GUI with the following widgets. ## ## *) There is an 'fRbuttons' frame to hold BUTTONS such as ## 'Exit' and 'Help' buttons --- as well as a 'ReDraw' button ## --- and a 'Reset' button to reset the display to an inital ## configuration --- and a 'Clear' button to clear the canvas. ## ## *) There is a SCALE widget that allows the user to set the ## chord on the circle --- via an angle between 0 and 180 degrees. ## ## *) There is a SCALE widget that allows the user to move the ## 'circumference point' along the circumference of the circle. ## ## *) There is a SCALE widget that allows the user to precisely ## set the size of the square canvas, in pixels. ## ## *) There is an 'fRinfo' frame may be used to hold some LABEL widgets, ## to show properties of the angle(s), such as size of the two ## angles in degrees and radians. ## ## *) There is an 'fRcanvas' frame to contain a CANVAS widgets that ## holds the drawing of the circle, chord, angles, and text. ## ## ##+######################### ## PLANNED LAYOUT OF THE GUI: ## ## FrameNames ## VVVVVVVVVV ## ----------------------------------------------------------------------------- ## tkCircleChord - Subtending Angle with Vertex at Center of Circle is Twice the Angle On Circumference ## [window title] ## ----------------------------------------------------------------------------- ## ## .fRbuttons {Exit} {Help} {ReDraw} {Show {Reset {Clear {Backgd {Line {Text {Write ## State} Parms} Canvas Color} Color} Color} PS File} ## ## .fRchord Position 0 180 Size of Square 300 4000 ## of Chord: <-----------O----------> Canvas (in pixels): <-------O-------> ## ## .fRcircum Position Vertex -50 +50 ## on Cirumference: <----------O---------> ## ## .fRinfo [ .......... to contain a stack of label widgets ......................... ] ## ## .fRcanvas ----------------------------------------------------------------------------A ## | | ## | | ## | | ## | [Area in which a Square Canvas (with scrollbars) is centered --- | ## | for displaying the circle, chord, and two subtending angles.] | ## | | ## | | ## | | ## | | ## <-------------------------------------------------------------------------->V ## ## SKETCH CONVENTIONS for this GUI sketch: ## ## SQUARE-BRACKETS indicate a comment (not to be placed on the GUI). ## BRACES indicate a Tk 'button' widget. ## ## A COLON indicates that the text before the colon is on a 'label' widget. ## <---O---> indicates a horizontal Tk 'scale' widget. ## UNDERSCORES indicate a Tk 'entry' widget (if any). ## CAPITAL-X indicates a Tk 'checkbutton' widget (if any). ## CAPITAL-O indicates a Tk 'radiobutton' widget (if any). ## ## A combination of VERTICAL-BAR CHARACTERS AND HYPHEN CHARACTERS, ## that outline a RECTANGULAR SHAPE, are used to indicate either a ## Tk 'canvas' widget or a Tk 'listbox' widget or a Tk 'text' widget. ## ## SCROLL-BAR 'ARROW-HEADS' (for a 'canvas', 'listbox', or 'text' Tk widget) ## are drawn as follows: ## ## UP ARROW-HEAD is drawn with a CAPITAL-A. ## DOWN ARROW-HEAD is drawn with a CAPITAL-V. ## LEFT ARROW-HEAD is drawn with a LESS-THAN sign. ## RIGHT ARROW-HEAD is drawn with a GREATER-THAN sign. ## ## UP-and-DOWN ARROW-HEADS at the right or left of the box shape indicate ## a VERTICAL SCROLL-BAR there. ## ## LEFT-and-RIGHT ARROW-HEADS at the bottom or top of the box shape indicate ## a HORIZONTAL SCROLL-BAR there. ## ## The arrow-heads on a horizontal scrollbar are joined by hyphens, rather than ## underscores. ## ## A LINE (HYPHENS or VERTICAL-BARS) WITH AN 'ARROW-HEAD' AT EACH END indicates ## a Tk 'scale' widget --- horizontal or vertical, respectively. ## ##+################## ## GUI WIDGET SUMMARY: ## ## This GUI will contain about: ## ## 10 'button' widgets ## 4 'label' widgets (at least) ## 2 'scale' widgets ## 1 'canvas' widget (with x-y scrollbars) ## ## 0 'entry' widgets ## 0 'checkbutton' widgets ## 0 'radiobutton' widgets ## 0 'listbox' widgets ## 0 'text' widgets ## ##+################################## ## METHOD USED to update the drawing: ## ## A set of drawing procs are used convert 'world coordinates' to ## pixel coordinates of the Tk canvas. ## ## The center of the circle is defined with center at 0.0,0.0 ## in 'world coordinates'. ## The circle has 'unit radius' in 'world coordinates'. ## ## The 'sin' and 'cos' functions can be used to set the end-points ## of the chord on the circle --- and to set the position of the ## 'circumference' point on the circle. ## ## The drawing procs are used to convert the 'world coordinates' ## to pixel coordinates of the Tk canvas and to draw lines and ## arcs. Some drawing procs are also provided to put some text items ## on the canvas, such as labels for vertices or values of angles. ## ## The names and descriptions of the drawing procs are in the ## PROCS section of the code, below. ## ##+####################### ## CAPTURING THE GUI IMAGE: ## ## A screen/window capture utility (like 'gnome-screenshot' ## on Linux) can be used to capture the GUI image in a PNG ## or GIF file, say. ## ## If necessary, an image editor (like 'mtpaint' on Linux) ## can be used to crop the window capture image. The image ## could also be down-sized --- say to make a smaller image ## suitable for use in a web page or an email. ## ## Alternatively, a 'WritePSfile' button on the GUI can be used to ## write a Postscript file of the image drawn on the Tk canvas. ## This works when the image is so large (in the scrollable canvas) ## that part of the image does not fit on the monitor screen. ## ##+####################################################################### ## 'CANONICAL' STRUCTURE OF THIS CODE: ## ## 0) Set general window parms (win-name, win-position, win-color-scheme, ## fonts, widget-geom-parms, win-size-control, text-array-for-labels-etc). ## ## 1a) Define ALL frames (and sub-frames, if any). ## 1b) Pack the frames. ## ## 2) Define & pack all widgets in the frames, frame by frame. ## After all the widgets for a frame are defined, pack them in the frame. ## ## 3) Define keyboard or mouse/touchpad/touch-sensitive-screen 'event' ## BINDINGS, if needed. ## ## 4) Define PROCS, if needed. ## ## 5) Additional GUI INITIALIZATION (typically with one or more of ## the procs), if needed. ## ##+################################# ## Some detail of the code structure of this particular script: ## ## 1a) Define ALL frames: ## ## Top-level : ## '.fRbuttons' - to contain 'Exit', 'Help', 'ReDraw', and other buttons ##. ## ## '.fRchord' - to contain a 'scale' widget to specify the ## position of the horizontal chord, and ## to contain a 'scale' widget to specify the ## width and height of the square canvas, in pixels. ## ## '.fRcircum' to contain a 'scale' widget to specify the ## position of the 'circumference point'. ## ## '.fRinfo' - to contain a stack of label widgets to contain ## info such as the central-angle (in degrees and radians) ## and the circumference-angle (in degrees and radians). ## ## '.fRcanvas' - to contain a square canvas widget with scrollbars, ## which will display the circle and angles. ## ## 1b) Pack ALL frames. ## ## 2) Define & pack all widgets in the frames -- basically going through ## frames & their interiors in left-to-right, or top-to-bottom order. ## ## 3) Define BINDINGS: such as button1-release on the 'scale' widgets ## For details, see the BINDINGS section in code below. ## ## 4) Define PROCS: ## ## 'ReDraw' - called by the 'ReDraw' button. ## ## 'draw_line_x1y1x2y2' - called by proc 'ReDraw'. ## ## 'draw_arc_x1y1_degStart_degExtent' - called by proc 'ReDraw'. ## ## 'draw_text_x1y1_center' - called by proc 'ReDraw' ## ## 'draw_point_x1y1_radiusPx' - called by proc 'ReDraw' (if used). ## ## 'setMappingVars_for_px2wc' - called by proc 'ReDraw'. ## ## 'Xwc2px' - called in the various draw procs. ## ## 'Ywc2px' - called in the various draw procs. ## ## 'advise_user' - called in the 'ReDraw' proc ## ## 'show_state' - called by the 'ShowState' button ## ## 'popup_msgVarWithScroll' - called by the 'Help' button, ## to show the HELPtext var. ## ## 5) Additional GUI Initialization, such as: ## - set the filename of the external RGB color-selector Tk script ## - initialize the 3 colors ## - call 'reset_parms' to set initial values of the params. ## - call 'ReDraw' to put an initial drawing on the canvas, for ## the given initial settings (of angles, etc.). ## ##+####################################################################### ## DEVELOPED WITH: Tcl-Tk 8.5 on Ubuntu 9.10 (2009-october, 'Karmic Koala') ## ## $ wish ## % puts "$tcl_version $tk_version" ## ## showed ## 8.5 8.5 ## but this script should work in most previous 8.x versions, and probably ## even in some 7.x versions (if font handling is made 'old-style'). ##+####################################################################### ## MAINTENANCE HISTORY: ## Started by: Blaise Montandon 2016nov22 Started the basic code of the ## script based on several previous ## 'tkGooies' scripts. ## Changed by: Blaise Montandon 2016nov26 Add a 'ShowState' button. ## Changed by: Blaise Montandon 2016dec26 Make a testable-releasable version ## of the 'show_state' proc. ##+######################################################################## ##+###################################################### ## Set WINDOW TITLE and POSITION. ##+###################################################### wm title . "tkCircleChord - Two Subtending Angles Theorem (Central & Circumference)" wm iconname . "tkCircleChord-2angles" wm geometry . +15+30 ##+###################################################### ## Set the COLOR SCHEME for the window and its widgets --- ## such as background color of 'scale' widgets. ##+###################################################### tk_setPalette "#f0f0f0" set scaleBKGD "#f0f0f0" # set listboxBKGD "#ffffff" # set entryBKGD "#ffffff" ##+######################################################## ## DEFINE (temporary) FONT NAMES. ## ## We use a VARIABLE-WIDTH font for text on LABEL and ## BUTTON widgets. ## ## We use a FIXED-WIDTH font for LISTBOX lists, ## for Help-text in a TEXT widget, and for ## the text in ENTRY fields, if any. ##+######################################################## font create fontTEMP_varwidth \ -family {comic sans ms} \ -size -14 \ -weight bold \ -slant roman font create fontTEMP_SMALL_varwidth \ -family {comic sans ms} \ -size -12 \ -weight bold \ -slant roman ## Some other possible (similar) variable width fonts: ## Arial ## Bitstream Vera Sans ## DejaVu Sans ## Droid Sans ## FreeSans ## Liberation Sans ## Nimbus Sans L ## Trebuchet MS ## Verdana font create fontTEMP_fixedwidth \ -family {liberation mono} \ -size -14 \ -weight bold \ -slant roman font create fontTEMP_SMALL_fixedwidth \ -family {liberation mono} \ -size -12 \ -weight bold \ -slant roman ## Some other possible fixed width fonts (esp. on Linux): ## Andale Mono ## Bitstream Vera Sans Mono ## Courier 10 Pitch ## DejaVu Sans Mono ## Droid Sans Mono ## FreeMono ## Nimbus Mono L ## TlwgMono ##+########################################################### ## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS. ## (e.g. width and height of canvas, and padding for Buttons) ##+########################################################### ## BUTTON widget geom settings: set PADXpx_button 0 set PADYpx_button 0 set BDwidthPx_button 2 ## LABEL widget geom settings: set PADXpx_label 0 set PADYpx_label 0 set BDwidthPx_label 2 ## SCALE widget geom parameters: set BDwidthPx_scale 2 set scaleThicknessPx 10 ## CANVAS widget geom settings: set initCanWidthPx 300 set initCanHeightPx 300 # set BDwidthPx_canvas 2 set BDwidthPx_canvas 0 ## Other widget geometry settings are not used, yet. ## COMMENTED, for now. if {0} { ## ENTRY widget geom settings: set BDwidthPx_entry 2 } ##+############################################################## ## Set a TEXT-ARRAY to hold text for buttons & labels on the GUI. ## NOTE: This can aid INTERNATIONALIZATION. This array can ## be set according to a nation/region parameter. ##+############################################################## ## if { "$VARlocale" == "en"} ## For '.fRbuttons' frame: set aRtext(buttonEXIT) "Exit" set aRtext(buttonHELP) "Help" set aRtext(buttonREDRAW) "ReDraw" set aRtext(buttonSTATE) "Show State" set aRtext(buttonRESET) "Reset Parms" set aRtext(buttonCLEAR) "Clear Canvas" set aRtext(buttonCOLORbkgd) "Backgrnd Color" set aRtext(buttonCOLORline) "Line Color" set aRtext(buttonCOLORtext) "Text Color" set aRtext(buttonWRITEPS) "Write PS file" ## For '.fRchord' frame: set aRtext(labelCHORD) "To Position the Chord (0 to 180 degrees):" set aRtext(labelIMGSIZE) " Size of Square Canvas (in pixels):" ## For '.fRcircum' frame: set aRtext(labelCIRCUM) "To Position the Vertex of the Angle on the Circumference (0 to 100 percent):" ## For messages to user: set aRtext(REPLOTmsg) "** Click 'ReDraw' when ALL parameter settings are ready." ## END OF if { "$VARlocale" == "en"} ##+###################################################################### ## Set a MIN-SIZE of the window (roughly). ## ## For WIDTH, allow for the min-width of the '.fRbuttons' frame ## --- at least the first 3 buttons. ## ## For HEIGHT, allow for the stacked frames: ## 2 chars high for the '.fRbuttons' frame, ## 2 chars high for the '.fRchord' frame, ## 2 chars high for the '.fRcircum' frame, ## 2 chars high for the '.fRinfo' frame, ## at least 50 pixels high for the '.fRcanvas' frame. ##+##################################################################### ## FOR WIDTH: set minWidthPx [font measure fontTEMP_varwidth \ " $aRtext(buttonEXIT) $aRtext(buttonHELP) $aRtext(buttonREDRAW) \ Reset Clear Color Color Color "] ## We add some pixels to account for right-left-size of ## window-manager decoration (~8 pixels) and some pixels for ## frame/widget borders (~8 widgets x 4 pixels/widget = 32 pixels). set minWinWidthPx [expr {40 + $minWidthPx}] ## For HEIGHT --- for ## 2 chars high for '.fRbuttons' ## 2 chars high for '.fRchord' ## 2 chars high for '.fRcircum' ## 2 chars high for '.fRinfo' (at least) ## 50 pixels high for '.fRcanvas' (at least) set charHeightPx [font metrics fontTEMP_varwidth -linespace] set minWinHeightPx [expr {8 * $charHeightPx}] ## Add about 50 pixels for height of the canvas ## AND add about 20 pixels for top-bottom window decoration -- ## and some pixels for top-and-bottom of frame/widget borders ## (~4 widgets x 4 pixels/widget = 16 pixels). set minWinHeightPx [expr {86 + $minWinHeightPx}] ## FOR TESTING: # puts "minWinWidthPx = $minWinWidthPx" # puts "minWinHeightPx = $minWinHeightPx" wm minsize . $minWinWidthPx $minWinHeightPx ## We may allow the window to be resizable. We pack the canvas ## (and the frames that contain them) with '-fill both -expand 1' ## so that the canvas can be enlarged by enlarging the window. ## If you want to make the window un-resizable, ## you can use the following statement. # wm resizable . 0 0 ##+################################################################ ## DEFINE *ALL* THE FRAMES: ## ## Top-level : '.fRbuttons' , '.fRchord' , '.fRcirccum', ## '.fRinfo' , '.fRcanvas' ## ## Sub-frames: none ##+################################################################ ## FOR TESTING change 0 to 1: ## (Example1: To see appearance of frames when borders are drawn.) ## (Example2: To see sizes of frames for various '-fill' options.) ## (Example3: To see how frames expand as window is resized.) if {0} { set RELIEF_frame raised set BDwidthPx_frame 2 } else { set RELIEF_frame flat set BDwidthPx_frame 0 } frame .fRbuttons -relief $RELIEF_frame -bd $BDwidthPx_frame # frame .fRchord -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRchord -relief raised -bd 2 # frame .fRcircum -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRcircum -relief raised -bd 2 # frame .fRinfo -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRinfo -relief raised -bd 2 # frame .fRcanvas -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRcanvas -relief raised -bd 2 ##+############################## ## PACK the FRAMES. ##+############################## pack .fRbuttons \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRchord \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRcircum \ -side top \ -anchor nw \ -fill none \ -expand 0 pack .fRinfo \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRcanvas \ -side top \ -anchor n \ -fill both \ -expand 1 ##+########################################################## ## The FRAMES ARE PACKED. START PACKING WIDGETS IN THE FRAMES. ##+########################################################## ##+########################################################## ## In FRAME '.fRbuttons' - ## DEFINE 'BUTTON' WIDGETS ## --- Exit, Help, ReDraw, ResetParms, ClearCanvas --- and ## 3 color-selector buttons. ## Then PACK them all. ##+########################################################## button .fRbuttons.buttEXIT \ -text "$aRtext(buttonEXIT)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {exit} button .fRbuttons.buttHELP \ -text "$aRtext(buttonHELP)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {popup_msgVarWithScroll .topHelp "$HELPtext" +30+30} button .fRbuttons.buttREDRAW \ -text "$aRtext(buttonREDRAW)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {ReDraw} button .fRbuttons.buttSTATE \ -text "$aRtext(buttonSTATE)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {show_state} button .fRbuttons.buttRESET \ -text "$aRtext(buttonRESET)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {reset_parms} button .fRbuttons.buttCLEAR \ -text "$aRtext(buttonCLEAR)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {clear_canvas} button .fRbuttons.buttCOLOR0 \ -text "$aRtext(buttonCOLORbkgd)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_background_color0" button .fRbuttons.buttCOLOR1 \ -text "$aRtext(buttonCOLORline)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_line_color1" button .fRbuttons.buttCOLOR2 \ -text "$aRtext(buttonCOLORtext)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_text_color2" button .fRbuttons.buttWRITEPS \ -text "$aRtext(buttonWRITEPS)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {writePSfile} ## Pack the widgets in frame '.fRbuttons'. pack .fRbuttons.buttEXIT \ .fRbuttons.buttHELP \ .fRbuttons.buttREDRAW \ .fRbuttons.buttSTATE \ .fRbuttons.buttRESET \ .fRbuttons.buttCLEAR \ .fRbuttons.buttCOLOR0 \ .fRbuttons.buttCOLOR1 \ .fRbuttons.buttCOLOR2 \ .fRbuttons.buttWRITEPS \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## In FRAME '.fRchord' - ## DEFINE two pairs of LABEL-and-SCALE widgets. ## Then PACK them all. ##+####################################################### label .fRchord.labelCHORD \ -text "$aRtext(labelCHORD)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Set this widget var in the 'Additional GUI Initialization' ## section at the bottom of this script --- by a call to the ## 'reset_parms' proc. # set CHORDdegrees 90.0 scale .fRchord.scaleCHORD \ -from 0.0 -to 180.0 \ -resolution 0.1 \ -font fontTEMP_SMALL_varwidth \ -variable CHORDdegrees \ -showvalue true \ -orient horizontal \ -bd $BDwidthPx_scale \ -length 180 \ -width $scaleThicknessPx ## Set the label-and-scale widgets for image size (pixels). label .fRchord.labelIMGSIZE \ -text "$aRtext(labelIMGSIZE)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Set this widget var in the 'Additional GUI Initialization' ## section at the bottom of this script --- by a call to the ## 'reset_parms' proc. # set IMGsizePx 600 scale .fRchord.scaleIMGSIZE \ -from 300 -to 4000 \ -resolution 1 \ -font fontTEMP_SMALL_varwidth \ -variable IMGsizePx \ -showvalue true \ -orient horizontal \ -bd $BDwidthPx_scale \ -length 180 \ -width $scaleThicknessPx ## Pack the widgets in frame '.fRchord': pack .fRchord.labelCHORD \ .fRchord.scaleCHORD \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRchord.scaleIMGSIZE \ .fRchord.labelIMGSIZE \ -side right \ -anchor e \ -fill none \ -expand 0 ##+######################################################## ## In FRAME '.fRcircum' - ## DEFINE one pair of LABEL-and-SCALE widgets. ## Then PACK them all. ##+####################################################### label .fRcircum.labelCIRCUM \ -text "$aRtext(labelCIRCUM)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Set this widget var in the 'Additional GUI Initialization' ## section at the bottom of this script --- by a call to the ## 'reset_parms' proc. # set CIRCUMpercent 50.0 scale .fRcircum.scaleCIRCUM \ -from 0.0 -to 100.0 \ -resolution 0.1 \ -font fontTEMP_SMALL_varwidth \ -variable CIRCUMpercent \ -showvalue true \ -orient horizontal \ -bd $BDwidthPx_scale \ -length 180 \ -width $scaleThicknessPx ## Pack the widgets in frame '.fRcircum': pack .fRcircum.labelCIRCUM \ .fRcircum.scaleCIRCUM \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## In FRAME '.fRinfo' - ## DEFINE TWO (or more) LABEL widgets. ## Then PACK them. ##+####################################################### set INFObgRGB "#ff3300" label .fRinfo.labelINFO1 \ -text "" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bg "$INFObgRGB" \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label label .fRinfo.labelINFO2 \ -text "" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bg "$INFObgRGB" \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Pack the widgets in frame '.fRinfo'. pack .fRinfo.labelINFO1 \ .fRinfo.labelINFO2 \ -side top \ -anchor nw \ -fill x \ -expand 0 ##+######################################################## ## In FRAME '.fRcanvas' - ## DEFINE a CANVAS WIDGET (with scrollbars). ## Then PACK them. ## ## We highlightthickness & borderwidth of the canvas to ## zero, as suggested on page 558, Chapter 37, 'The Canvas ## Widget', in the 4th edition of the book 'Practical ## Programming in Tcl and Tk'. ##+####################################################### canvas .fRcanvas.can \ -width $initCanWidthPx \ -height $initCanHeightPx \ -relief flat \ -highlightthickness 0 \ -borderwidth 0 \ -yscrollcommand {.fRcanvas.scrolly set} \ -xscrollcommand {.fRcanvas.scrollx set} scrollbar .fRcanvas.scrolly \ -orient vertical \ -command {.fRcanvas.can yview} scrollbar .fRcanvas.scrollx \ -orient horizontal \ -command {.fRcanvas.can xview} ##+####################################################### ## Pack the widgets in frame '.fRcanvas'. ##+####################################################### ##+######################################################## ## NOTE: ## NEED TO PACK THE SCROLLBARS BEFORE THE CANVAS WIDGET. ## OTHERWISE THE CANVAS WIDGET TAKES ALL THE FRAME SPACE. ##+####################################################### pack .fRcanvas.scrolly \ -side right \ -anchor e \ -fill y \ -expand 0 pack .fRcanvas.scrollx \ -side bottom \ -anchor s \ -fill x \ -expand 0 ##+############################################################## ## !!!NEED TO USE '-expand 0' FOR THE X AND Y SCROLLBARS, so that ## the canvas is allowed to fill the remaining frame-space nicely ## --- without a gap between the canvas and its scrollbars. ##+############################################################## pack .fRcanvas.can \ -side top \ -anchor n \ -fill none \ -expand 0 ##+################################################## ## END OF DEFINITION of the GUI widgets. ##+################################################## ## Start of BINDINGS, PROCS, Added-GUI-INIT sections. ##+################################################## ##+################################################################## ##+################################################################## ## BINDINGS SECTION: ## We COULD put button1-release bindings on the 'scale' widgets. ##+################################################################## bind .fRchord.scaleCHORD { advise_user "A NEW CHORD POSITION may have been selected. $aRtext(REPLOTmsg)" } bind .fRchord.scaleIMGSIZE { advise_user "A NEW IMAGE SIZE may have been selected. $aRtext(REPLOTmsg)" } bind .fRcircum.scaleCIRCUM { advise_user "A NEW CIRCUMFERENCE VERTEX POSITION may have been selected. $aRtext(REPLOTmsg)" } ##+################################################################## ##+################################################################## ## DEFINE PROCS SECTION: ## ## 'ReDraw' - Called by the 'ReDraw' button. ## This proc uses the draw procs below to ## erase and redraw the oval, lines,text,etc. ## on the canvas --- for example, the user ## has changed a group of drawing parameters ## (scale widgets, colors). ## ## 'draw_circle_x1y1_radius' - Called by the 'ReDraw' proc. ## Given x1,y1 (the center of the circle) and ## its radius (in 'world coordinates'), ## this proc draws a circle on the canvas. ## (A hex-RGB color specification is also input, ## to specify the color of the circle.) ## (The arc drawn may be passed a tag, such as ## 'TAGcircle'.) ## ## (Note: Conversion from 'world coordinates' to ## pixels is done within this proc --- and in ## the following 'draw' procs. The global variables, ## that were set by a call to the proc ## 'setMappingVars_for_px2wc', are used to do the ## conversion.) ## ## 'draw_line_x1y1x2y2' - Called by the 'ReDraw' proc. ## Given x1,y1 and x2,y2 in 'world coordinates', ## this proc draws a line on the canvas. ## (A hex-RGB color specification is also input, ## to specify the color of the line.) ## (The lines drawn may be passed a tag, such as ## 'TAGline'.) ## ## 'draw_text_x1y1_center' - Called by the 'ReDraw' proc. ## Given x1,y1 and a text string, this proc ## draws the text string on the canvas. ## (A hex-RGB color specification is also input, ## to specify the color of the text.) ## (The text drawn may be passed a tag, such as ## 'TAGtext'.) ## ## 'draw_point_x1y1_radiusPx' - Called by the 'ReDraw' proc (if this is used). ## Given x1,y1 and a radius (in pixels), this ## proc draws a circle on the canvas, centered ## at x1,y1 and with the specified radius. ## Radius zero causes a single pixel to be drawn. ## (A hex-RGB color specification is also input, ## to specify the color of the filled circle ## --- or the single pixel. If we use this proc, ## we may use the text color for the point color.) ## (The point drawn may be passed a tag, such as ## 'TAGpoint'.) ## ## 'setMappingVars_for_px2wc' - Called by the 'ReDraw' proc. ## This proc sets up constants to be used in converting ## a 'world coordinate' to a 'pixel coordinate' on ## the Tk canvas. ## ## This proc takes the coordinates of an UpperLeft (UL) ## point and a LowerRight (LR) point --- in both ## 'world coordinates' and 'pixel coordinates' and ## sets some global variables to the used by the ## other drawing procs --- mainly the ratio: ## ## the number-of-pixels-per-world-coordinate-unit ## ## (This will generally include a fractional amount, ## i.e. it is not necessarily an integer.) ## ## At least eight numbers are input to this proc, as indicated by: ## ## ULwcX ULwcY ULpxX ULpxY LRwcX LRwcY LRpxX LRpxY ## ## Generally, the 'wc' inputs may be floating point numbers, and the ## 'px' inputs will generally be (non-negative) integers. ## ## Example: ## -1.2 1.2 0 0 1.2 -0.2 $canvasWidthPx $canvasHeightPx ## ## 'Xwc2px' - ## Converts an x world-coordinate to pixel units. ## ## 'Ywc2px' - Called by the various 'draw' procs. ## Converts a y world-coordinate to pixel units. ## ## 'set_background_color0' - Called by the 'BkgdColor' button. ## Sets the color for the canvas (background). ## ## 'set_line_color1' - Called by the 'LineColor' button. ## Sets the color for drawing lines (including circles) ## on the canvas. ## ## 'set_text_color2' - Called by the 'TextColor' button. ## Sets the color for drawing text on the canvas. ## ## 'update_button_colors' - Called by the several 'set_*_color*' procs. ## Sets the color of the 3 color buttons. ## ## 'advise_user' - Called by the 'ReDraw' proc and in the ## 'Additional GUI Initialization' section at ## the bottom of the script. ## ## 'show_state' - Called by the 'ShowState' button. ## Calculates various lengths and angles in ## the current drawing on the canvas. ## ## 'writePSfile' - Called by the 'WritePSfile' button. ## Writes a (color)Postscript file from the ## current drawing on the canvas. ## ## 'popup_msgVarWithScroll' - Called by the 'Help' button to show the ## 'HELPtext' variable. ## ##+################################################################# ##+############################################################# ## proc 'ReDraw' ## ## PURPOSE: Used to erase and redraw the lines,arcs,text,etc. ## on the canvas --- for the semi-circle and the ## inscribed triangle --- for example, ## after the user has resized the GUI window. ## ## This proc also is used to update the text in ## the labels of the '.fRinfo' frame. ## ## CALLED BY: The 'Additional GUI Initialization' section ## and a button1-release binding on the scale widget. ##+############################################################# proc ReDraw {} { ## FOR TESTING: (to dummy out this proc) # return global radsPERdeg pi CHORDdegrees IMGsizePx CIRCUMpercent \ decimalDIGITS integerDIGITS \ COLOR0hex COLOR1hex COLOR2hex ## Calculated variables to be re-used in the 'show_state' proc. global ChordX1 ChordY1 ChordX2 ChordY2 xPeak yPeak ChordLen CentralDegs CentralRads ############################################################ ## Set the dimensions of the canvas according to the requested ## size, IMGsizePx, in pixels. ############################################################ .fRcanvas.can configure -width $IMGsizePx .fRcanvas.can configure -height $IMGsizePx ############################################################# ## Use 'scrollregion' to make the canvas scrollbars usable ## for a large image. ## ## We want to size the scroll region (in pixels) according to ## the requested region size --- IMGsizePx. ############################################################# .fRcanvas.can configure -scrollregion "0 0 $IMGsizePx $IMGsizePx" ######################################################## ## Make the canvas area as big as we can to accomodate ## a large image. ## (We use a 'wm' command to get the window to resize ## according to the canvas resize --- even after the ## user has manually resized the top window.) ## ## Reference: wiki.tcl.tk/10720 and wiki.tcl.tk/44 ## and page 237 of Ousterhout's book 'Tcl and the Tk Toolkit': ## "If you would like to restore a window to its natural ## size, you can invoke 'wm geometry' with an empty ## geometry string." ######################################################## wm geometry . {} ##+############################################################ ## Set the background/canvas color. ##+############################################################ .fRcanvas.can config -bg $COLOR0hex ################################################################ ## Set the variables for converting world-coords to pixels. ################################################################ ## Recall input vars: ## setMappingVars_for_px2wc(xORy,ULwcX,ULwcY,ULpxX,ULpxY,LRwcX,LRwcY,LRpxX,LRpxY) ## ## We map world-coord X-limits -1.2 and 1.2 ## TO pixel-coord X-limits 0 and IMGsizePx. ## ## OR ## ## We map world-coord Y-limits 1.2 and -1.2 ## TO pixel-coord Y-limits 0 and IMGsizePx. ## ## See code in 'setMappingVars_for_px2wc' for details. ######################################################################## setMappingVars_for_px2wc xy -1.2 1.2 0 0 1.2 -1.2 $IMGsizePx $IMGsizePx ################################################################ ## Remove any previously drawn elements in this canvas, if any. ################################################################ ## If the window was not resized, the semi-circle would not ## have to be redrawn --- BUT checking whether the window is ## resized may take almost as much processing as simply redrawing ## the semi-circle. So we simply delete all geometry and redraw. ################################################################# catch {.fRcanvas.can delete all} ################################################################ ## Draw the circle --- with unit radius. ## ## NOTE: The proc 'draw_circle_x1y1_radius' takes x1,y1,radius ## in 'world coordinates'. ################################################################ draw_circle_x1y1_radius 0.0 0.0 1.0 $COLOR1hex TAGcircle ################################################################ ## Draw the horizontal chord according to CHORDdegrees. ## ## The endpoints of the chord are at (270 - CHORDdegrees) and ## (270 + CHORDdegrees) on the circumference of the circle. ## ## NOTE: The proc 'draw_line_x1y1x2y2' takes x1,y1,x2,y2 ## in 'world coordinates'. ################################################################ set TEMPdeg [expr {270.0 - $CHORDdegrees}] set rads [expr {$TEMPdeg * $radsPERdeg}] set ChordX1 [expr {cos($rads)}] set ChordY1 [expr {sin($rads)}] set TEMPdeg [expr {270.0 + $CHORDdegrees}] set rads [expr {$TEMPdeg * $radsPERdeg}] set ChordX2 [expr {cos($rads)}] set ChordY2 [expr {sin($rads)}] draw_line_x1y1x2y2 $ChordX1 $ChordY1 $ChordX2 $ChordY2 $COLOR1hex TAGline ################################################################### ## Draw the 2 legs for the 'central angle' subtending the chord. ## Draw from the center of the circle to the endpoints of the chord ## --- ChordX1,ChordY1 and ChordX2,ChordY2. ## ## NOTE: The proc 'draw_line_x1y1x2y2' takes x1,y1,x2,y2 ## in 'world coordinates'. ################################################################### draw_line_x1y1x2y2 0.0 0.0 $ChordX1 $ChordY1 $COLOR1hex TAGline draw_line_x1y1x2y2 0.0 0.0 $ChordX2 $ChordY2 $COLOR1hex TAGline ################################################################# ## Calculate the vertex point of the 'circumference angle', ## according to CIRCUMpercent. We let: ## CICUMpercent = 0 mean the right-end of the chord --- ChordX2,ChordY2 ## --- at angle (270 + CHORDdegrees) = (-90 + CHORDdegrees). ## CICUMpercent = 100 mean the left-end of the chord --- ChordX1,ChordY1 ## --- at angle (270 - CHORDdegrees). ## ## We use CIRCUMpercent to calculate an angle between ## A = (-90 + CHORDdegrees) and B = (270 - CHORDdegrees). ## B - A = (270 - CHORDdegrees) - (-90 + CHORDdegrees) ## = 360 - 2 * CHORDdegrees ## We want angle C = A + CIRCUMpercent/100 * (B - A) ## = (-90 + CHORDdegrees) + CIRCUMpercent/100 * (360 - 2 * CHORDdegrees) ## ## As a check on the formula we use: ## CICUMpercent = 50 means the top of the circle --- 0.0,1.0 ## --- at angle 90 degrees. For CIRCUMpercent = 50, the formula ## should give us angle 90 degrees. This checks out. ################################################################# set A [expr {-90.0 + $CHORDdegrees}] set B [expr {270.0 - $CHORDdegrees}] set TEMPdeg [expr {$A + ($CIRCUMpercent/100.0) * ($B - $A)}] set rads [expr {$TEMPdeg * $radsPERdeg}] set xPeak [expr {cos($rads)}] set yPeak [expr {sin($rads)}] ################################################################ ## Draw the 2 legs from the vertex of the 'circumference angle' ## to the endpoints of the chord --- ChordX1,ChordY1 and ChordX2,ChordY2. ## ## NOTE: The proc 'draw_line_x1y1x2y2' takes x1,y1,x2,y2 ## in 'world coordinates'. ################################################################ draw_line_x1y1x2y2 $ChordX1 $ChordY1 $xPeak $yPeak $COLOR1hex TAGline draw_line_x1y1x2y2 $ChordX2 $ChordY2 $xPeak $yPeak $COLOR1hex TAGline ################################################################ ## Calculate half the central angle --- which is ## arcsin(0.5*ChordLength) --- in radians and degrees --- ## when the chord is below the center of the circle. ## If the chord is above the center of the circle, half the ## cnetral angle is pi - arcsin(0.5*ChordLength). ################################################################ # set ChordLen [expr {hypot($ChordX2 - $ChordX1, $ChordY2 - $ChordY1)}] set ChordLen [expr {abs($ChordX2 - $ChordX1)}] set halfCentralRads [expr {asin(0.5 * $ChordLen)}] if {$ChordY1 > 0.0} {set halfCentralRads [expr {$pi - $halfCentralRads}]} set halfCentralDegs [expr {$halfCentralRads / $radsPERdeg}] ################################################################ ## Calculate the central angle --- in radians and degrees. ################################################################ set CentralDegs [expr {2.0 * $halfCentralDegs}] set CentralRads [expr {2.0 * $halfCentralRads}] ################################################################ ## Add a text label near the vertex of the 'circumference angle' ## --- the letter 'C'. ## ## NOTE: The proc 'draw_text_x1y1_center' takes x1,y1 ## in 'world coordinates'. ################################################################ set xtext [expr {0.90 * $xPeak}] # set ytext [expr {$yPeak - 0.22}] set ytext [expr {$yPeak + 0.10}] draw_text_x1y1_center $xtext $ytext $COLOR2hex TAGtext "C" ################################################################ ## Add a text label near the vertex of the 'central angle', ## which is at the center of the circle -- the letter 'A'. ## ## NOTE: The proc 'draw_text_x1y1_center' takes x1,y1 ## in 'world coordinates'. ################################################################ set xtext 0.0 set ytext -0.20 draw_text_x1y1_center $xtext $ytext $COLOR2hex TAGtext "A" ################################################################ ## Add a text label near the chord --- the word 'Chord'. ## ## Put the text above the chord when the chord is below the ## center of the circle, and below the chord otherwise. ## ## NOTE: The proc 'draw_text_x1y1_center' takes x1,y1 ## in 'world coordinates'. ################################################################ set xtext 0.0 if {$ChordY1 < 0.0} { set ytext [expr {$ChordY1 + 0.10}] } else { set ytext [expr {$ChordY1 - 0.10}] } draw_text_x1y1_center $xtext $ytext $COLOR2hex TAGtext "Chord" ################################################################ ## Add a text label near the top of the image --- pointing out ## that "AngleC = AngleA / 2". ## ## NOTE: The proc 'draw_text_x1y1_center' takes x1,y1 ## in 'world coordinates'. ################################################################ set xtext 0.70 set ytext 1.1 draw_text_x1y1_center $xtext $ytext $COLOR2hex TAGtext "AngleC = AngleA / 2" ################################################################ ## Add a point (small circle) to the drawing --- at the origin ## (the center of the circle) --- radius 3 pixels. ## COMMENTED, for now. ################################################################ # draw_point_x1y1_radiusPx 0.0 0.0 3 $COLOR2hex TAGpoint ################################################################ ## Add a '1' text label on each side of the center point --- ## to indicate that the radius of the semi-circle is one. ## COMMENTED, for now. ################################################################ # draw_text_x1y1_center 0.5 -0.1 $COLOR2hex TAGtext "1.0" # draw_text_x1y1_center -0.5 -0.1 $COLOR2hex TAGtext "1.0" ################################################################ ## Put the 'central angle' (in degrees and radians) and ## the 'circumference angle' (in degrees and radians) in a label ## in the 'info' frame. ################################################################ set CentralDegsFMTD [format "%${integerDIGITS}.${decimalDIGITS}f" $CentralDegs] set CentralRadsFMTD [format "%${integerDIGITS}.${decimalDIGITS}f" $CentralRads] set halfCentralDegsFMTD [format "%${integerDIGITS}.${decimalDIGITS}f" $halfCentralDegs] set halfCentralRadsFMTD [format "%${integerDIGITS}.${decimalDIGITS}f" $halfCentralRads] .fRinfo.labelINFO1 configure -text \ "Central Angle at A: $CentralDegsFMTD degrees or $CentralRadsFMTD radians Circumference Angle at C: $halfCentralDegsFMTD degrees or $halfCentralRadsFMTD radians" #################################################################### ## Put a verbal observation in a label in the 'info' frame. #################################################################### .fRinfo.labelINFO2 configure -text \ "**** NOTE: The Angle At C is always half The Angle At A." } ## END OF proc 'ReDraw' ##+######################################################################## ## PROC 'draw_circle_x1y1_radius' ##+######################################################################## ## PURPOSE: Given x1,y1 and a radius (in 'world coordinates'), ## this proc draws a circle on the canvas --- centered at x1,y1 ## and with a specified radius. ## ## (A hex-RGB color specification is also input, ## to specify the color of the arc.) ## ## (The circle drawn may be passed a tag, such as ## 'TAGcircle'.) ## ## CALLED BY: proc 'ReDraw' ##+######################################################################## proc draw_circle_x1y1_radius {x1 y1 radius hexRGB tagID} { ## FOR TESTING: (dummy out this proc) # return global PXperWCx lineWidthPx radsPERdeg ####################################################### ## Convert the radius from world coord units to pixels. ####################################################### set radiusPx [expr {$radius * $PXperWCx}] ############################################### ## Convert x1,y1 world-coord units to pixels. ############################################### set x1Px [Xwc2px $x1] set y1Px [Ywc2px $y1] ## FOR TESTING: if {0} { puts "proc 'draw_circle_x1y1_radius':" puts "x1: $x1 y1: $y1 radius: $radius" } ################################################################## ## Set the corner coords for the circle that defines the arc. ################################################################## set topleftXpx [expr {$x1Px - $radiusPx}] set topleftYpx [expr {$y1Px - $radiusPx}] set botrightXpx [expr {$x1Px + $radiusPx}] set botrightYpx [expr {$y1Px + $radiusPx}] ######################################################## ## Draw the circle. ######################################################## .fRcanvas.can create oval \ $topleftXpx $topleftYpx $botrightXpx $botrightYpx \ -outline $hexRGB -width $lineWidthPx -tag $tagID } ## END OF proc 'draw_circle_x1y1_radius' ##+######################################################################## ## PROC 'draw_line_x1y1x2y2' ##+######################################################################## ## PURPOSE: Given x1,y1 and x2,y2 in 'world coordinates', ## this proc draws a line on the canvas. ## ## (A hex-RGB color specification is also input, ## to specify the color of the line.) ## ## (The lines drawn may be passed a tag, such as ## 'TAGline'.) ## ## (Note: Conversion from 'world coordinates' to ## pixels is done within this proc --- and in ## the following 'draw' procs. The global variables, ## that were set by a call to the proc ## 'setMappingVars_for_px2wc', are used to do the ## conversion.) ## ## CALLED BY: proc 'ReDraw' ##+####################################################################### proc draw_line_x1y1x2y2 {x1 y1 x2 y2 hexRGB tagID} { ## FOR TESTING: (dummy out this proc) # return global lineWidthPx ############################################### ## Convert world-coords to pixels. ############################################### set x1Px [Xwc2px $x1] set y1Px [Ywc2px $y1] set x2Px [Xwc2px $x2] set y2Px [Ywc2px $y2] ## FOR TESTING: if {0} { puts "proc 'draw_line_x1y1x2y2':" puts "x1: $x1 y1: $y1 x1Px: $x1Px y1Px: $y1Px" puts "x2: $x2 y2: $y2 x2Px: $x2Px y2Px: $y2Px" } ##################################### ## Draw the line. ##################################### .fRcanvas.can create line \ $x1Px $y1Px $x2Px $y2Px \ -fill $hexRGB -width $lineWidthPx -tags $tagID } ## END OF proc 'draw_line_x1y1x2y2' ##+######################################################################## ## PROC 'draw_text_x1y1_center' ##+######################################################################## ## PURPOSE: Given x1,y1 and a text string, this proc ## this proc draws the text string on the canvas. ## ## (A hex-RGB color specification is also input, ## to specify the color of the text.) ## ## (The text drawn may be passed a tag, such as ## 'TAGtext'.) ## ## CALLED BY: proc 'ReDraw' ##+######################################################################## proc draw_text_x1y1_center {x1 y1 hexRGB tagID textSTRING} { ## FOR TESTING: (dummy out this proc) # return # global lineWidthPx ############################################### ## Convert x1,y1 world-coord units to pixels. ############################################### set x1Px [Xwc2px $x1] set y1Px [Ywc2px $y1] ## FOR TESTING: if {0} { puts "proc 'draw_text_x1y1_center':" puts "x1: $x1 y1: $y1 x1Px: $x1Px y1Px: $y1Px" } ###################################### ## Draw the text string. ###################################### .fRcanvas.can create text \ $x1Px $y1Px \ -anchor center -justify center -fill $hexRGB \ -text $textSTRING -font fontTEMP_SMALL_varwidth } ## END OF proc 'draw_text_x1y1_center' ##+######################################################################## ## PROC 'draw_point_x1y1_radiusPx' ##+######################################################################## ## PURPOSE: Given x1,y1 and a radius (in pixels), this ## proc draws a circle on the canvas, centered ## at x1,y1 and with the specified radius. ## ## Radius zero causes a single pixel to be drawn. ## ## (A hex-RGB color specification is also input, ## to specify the color of the filled circle ## --- or the single pixel.) ## ## (The point drawn may be passed a tag, such as ## 'TAGpoint'.) ## ## CALLED BY: the 'ReDraw' proc ##+######################################################################## proc draw_point_x1y1_radiusPx {x1 y1 radiusPx hexRGB tagID} { ## FOR TESTING: (dummy out this proc) # return global lineWidthPx drawRGB ############################################### ## Convert world-coords to pixels. ############################################### set x1Px [Xwc2px $x1] set y1Px [Ywc2px $y1] ## FOR TESTING: if {0} { puts "proc 'draw_point_x1y1_radiusPx':" puts "x1: $x1 y1: $y1 x1Px: $x1Px y1Px: $y1Px" } ################################################################## ## Set the corner coords for the (small) circle. ################################################################## set topleftXpx [expr {$x1Px - $radiusPx}] set topleftYpx [expr {$y1Px - $radiusPx}] set botrightXpx [expr {$x1Px + $radiusPx}] set botrightYpx [expr {$y1Px + $radiusPx}] ################################################ ## Draw a circle at x1,y1. ################################################ .fRcanvas.can create oval \ $topleftXpx $topleftYpx $botrightXpx $botrightYpx \ -fill $hexRGB -width $lineWidthPx -outline {} -tag $tagID ## If we want an outline around the circle, use something like this: # -width $lineWidthPx -outline $drawRGB } ## END OF proc 'draw_point_x1y1_radiusPx' ##+######################################################################## ## PROC: setMappingVars_for_px2wc ##+######################################################################## ## PURPOSE: Sets up 'constants' to be used in converting between x,y ## 'world coordinates' and 'pixel coordinates' on a Tk canvas. ## ## Puts the constants in global variables: ## PXperWC BASEwcX BASEwcY BASEpxX BASEpxY ## ## These variables are for use by 'Xwc2px' and 'Ywc2px' procs ## and 'Xpx2wc' and 'Ypx2wc' procs. ## ## The 'BASE' variables are coordinates of the upper-left point ## of the 'drawting rectangle' --- in world coordinates and ## in pixel coordinates. ## ## METHOD: This proc takes the coordinates of an UpperLeft (UL) ## point and a LowerRight (LR) point --- in both ## 'world coordinates' and 'pixel coordinates' and ## sets some global variables to the used by the ## other drawing procs --- mainly the ratio: ## ## the number-of-pixels-per-world-coordinate-unit, ## in global variable 'PXperWC' ## ## (This will generally include a fractional amount, ## i.e. it is not necessarily an integer.) ## ## INPUTS: ## ## At least eight numbers are input to this proc, as indicated by: ## ## ULwcX ULwcY ULpxX ULpxY LRwcX LRwcY LRpxX LRpxY ## ## Generally, the 'wc' inputs may be floating point numbers, and the ## 'px' inputs will generally be (non-negative) integers. ## ## Example: (for a draw area with x between -1.2 and +1.2 ## and with y between -0.2 and +1.2) ## setMappingVars_for_px2wc xy -1.2 1.2 0 0 1.2 -0.2 $canvasWidthPx $canvasHeightPx ## ## The first argument can be either 'x' or 'y' or 'xy'. This determines whether ## global variable 'PXperWC' is detemined by just the X-numbers, just the Y-numbers, ## or both. In this script, we use 'xy' (both). ## ## An 'adjustYpx' global variable can be used to adjust if the pixels ## on a user's monitor are not square. ## ## OUTPUTS: global variables PXperWC BASEwcX BASEwcY BASEpxX BASEpxY ## ## CALLED BY: by the animate' proc. ##+######################################################################## proc setMappingVars_for_px2wc {xORy ULwcX ULwcY ULpxX ULpxY LRwcX LRwcY LRpxX LRpxY} { global PXperWCx PXperWCy BASEwcX BASEwcY BASEpxX BASEpxY adjustYpx ## FOR TESTING: (to dummy out this proc) # return ############################################################ ## Calculate PXperWCx and PXperWCy ## (pixels-per-world-coordinate-unit) --- the ratio ## of pixels-per-world-coordinate in the x and y directions, ## for the given UL and LR values. ############################################################ set PXperWCx [expr {abs(($LRpxX - $ULpxX) / ($LRwcX - $ULwcX))}] set PXperWCy [expr {abs(($LRpxY - $ULpxY) / ($LRwcY - $ULwcY))}] ## FOR TESTING: if {0} { puts "proc 'setMappingVars_for_px2wc':" puts "LRwcY: $LRwcY ULwcY: $ULwcY LRwcX: $LRwcX ULwcX: $ULwcX" puts "PXperWCx: $PXperWCx" puts "LRpxY: $LRpxY ULpxY: $ULpxY LRpxX: $LRpxX ULpxX: $ULpxX" puts "PXperWCy: $PXperWCy" } ############################################################# ## Reset PXperWCx and PXperWCy according to whether input ## variable 'xORy' is 'x' or 'y' or 'min' or 'xy'. ## ## For 'x', we set PXperWCy equal to PCperWcx. ## ## For 'y', we set PXperWCx equal to PCperWcy. ## ## For 'min', we set PXperWCx and PXperWCy to the smaller ## of PXperWCx and PXperWCy. ## ## For 'max', we set PXperWCx and PXperWCy to the larger ## of PXperWCx and PXperWCy. ## ## For 'xy', we will leave PXperWCx and PXperWCy unchanged. ############################################################# if {$xORy == "x"} { set PXperWCy $PXperWCx } elseif {$xORy == "y"} { set PXperWCx $PXperWCy } elseif {$xORy == "min"} { if {$PXperWCx > $PXperWCy} { set PXperWCx $PXperWCy } else { set PXperWCy $PXperWCx } } elseif {$xORy == "max"} { if {$PXperWCx > $PXperWCy} { set PXperWCy $PXperWCx } else { set PXperWCx $PXperWCy } } ## END OF if {$xORy == "x"} ############################################################ ## In case the pixels are not square, provide a factor ## that can be used to adjust in the Y direction. ############################################################ set adjustYpx 1.0 ############################################################ ## Set BASEwcX, BASEwcY, BASEpxX and BASEpxY. ############################################################ set BASEwcX $ULwcX set BASEwcY $ULwcY set BASEpxX $ULpxX set BASEpxY $ULpxY ## FOR TESTING: if {0} { puts "proc 'setMappingVars_for_px2wc':" puts "PXperWCx: $PXperWCx PXperWCy: $PXperWCy" puts "BASEwcX: $BASEwcX BASEwcY: $BASEwcY" puts "BASEpxX: $BASEpxX BASEpxY: $BASEpxY" } } ## END OF PROC 'setMappingVars_for_px2wc' ##+######################################################################## ## PROC 'Xwc2px' ##+######################################################################## ## PURPOSE: Converts an x world-coordinate to pixel units. ## ## CALLED BY: the 'draw' procs ##+######################################################################## proc Xwc2px {x} { global PXperWCx BASEwcX BASEwcY BASEpxX BASEpxY adjustYpx set px [expr {($x - $BASEwcX) * $PXperWCx + $BASEpxX}] return $px } ## END OF proc 'Xwc2px' ##+######################################################################## ## PROC 'Ywc2px' ##+######################################################################## ## PURPOSE: Converts an y world-coordinate to pixel units. ## ## CALLED BY: the 'draw' procs ##+######################################################################## proc Ywc2px {y} { global PXperWCy BASEwcX BASEwcY BASEpxX BASEpxY adjustYpx set px [expr {($BASEwcY - $y) * $PXperWCy * $adjustYpx + $BASEpxY}] return $px } ## END OF proc 'Ywc2px' ##+##################################################################### ## PROC: 'set_background_color0' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set a canvas-background color. ## ## Arguments: global variables ## ## CALLED BY: .fRbuttons.buttCOLOR0 button ##+##################################################################### proc set_background_color0 {} { global COLOR0r COLOR0g COLOR0b COLOR0hex ColorSelectorScript aRtext ## FOR TESTING: # puts "COLOR0r: $COLOR0r" # puts "COLOR0g: $COLOR0g" # puts "COLOR0b: $COLOR0b" set TEMPrgb [ exec $ColorSelectorScript $COLOR0r $COLOR0g $COLOR0b] ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLOR0hex "#$hexRGB" set COLOR0r $r255 set COLOR0g $g255 set COLOR0b $b255 ## Set background-and-foreground colors of the indicated color button. update_color_buttons color0 advise_user "A NEW BACKGROUND COLOR may have been selected. $aRtext(REPLOTmsg)" } ## END OF proc 'set_background_color0' ##+##################################################################### ## PROC: 'set_line_color1' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set a canvas-line-segment color. ## ## Arguments: global variables ## ## CALLED BY: .fRbuttons.buttCOLOR1 button ##+##################################################################### proc set_line_color1 {} { global COLOR1r COLOR1g COLOR1b COLOR1hex ColorSelectorScript aRtext ## FOR TESTING: # puts "COLOR1r: $COLOR1r" # puts "COLOR1g: $COLOR1g" # puts "COLOR1b: $COLOR1b" set TEMPrgb [ exec $ColorSelectorScript $COLOR1r $COLOR1g $COLOR1b] ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLOR1hex "#$hexRGB" set COLOR1r $r255 set COLOR1g $g255 set COLOR1b $b255 ## Set background-and-foreground colors of the indicated color button. update_color_buttons color1 advise_user "A NEW LINE COLOR may have been selected. $aRtext(REPLOTmsg)" } ## END OF proc 'set_line_color1' ##+##################################################################### ## PROC: 'set_text_color2' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set a text-on-canvas color. ## ## Arguments: global variables ## ## CALLED BY: .fRbuttons.buttCOLOR2 button ##+##################################################################### proc set_text_color2 {} { global COLOR2r COLOR2g COLOR2b COLOR2hex ColorSelectorScript aRtext ## FOR TESTING: # puts "COLOR2r: $COLOR2r" # puts "COLOR2g: $COLOR2g" # puts "COLOR2b: $COLOR2b" set TEMPrgb [ exec $ColorSelectorScript $COLOR2r $COLOR2g $COLOR2b] ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLOR2hex "#$hexRGB" set COLOR2r $r255 set COLOR2g $g255 set COLOR2b $b255 ## Set background-and-foreground colors of the indicated color button. update_color_buttons color2 advise_user "A NEW TEXT COLOR may have been selected. $aRtext(REPLOTmsg)" } ## END OF proc 'set_text_color2' ##+##################################################################### ## PROC: 'update_color_buttons' ##+##################################################################### ## PURPOSE: ## This procedure is invoked to set the background color of the ## color button, indicated by the 'colorID' string, ## to its currently set 'colorID' color --- and sets ## foreground color, for text on the button, to a suitable black or ## white color, so that the label text is readable. ## ## Arguments: global color vars ## ## CALLED BY: in 3 'set_*_color*' procs ## and in the additional-GUI-initialization section at ## the bottom of this script. ##+##################################################################### proc update_color_buttons {colorID} { global COLOR0r COLOR0g COLOR0b COLOR0hex global COLOR1r COLOR1g COLOR1b COLOR1hex global COLOR2r COLOR2g COLOR2b COLOR2hex # set colorBREAK 300 set colorBREAK 350 if {"$colorID" == "color1"} { .fRbuttons.buttCOLOR1 configure -bg $COLOR1hex set sumCOLOR1 [expr {$COLOR1r + $COLOR1g + $COLOR1b}] if {$sumCOLOR1 > $colorBREAK} { .fRbuttons.buttCOLOR1 configure -fg "#000000" } else { .fRbuttons.buttCOLOR1 configure -fg "#ffffff" } } elseif {"$colorID" == "color2"} { .fRbuttons.buttCOLOR2 configure -bg $COLOR2hex set sumCOLOR1 [expr {$COLOR2r + $COLOR2g + $COLOR2b}] if {$sumCOLOR1 > $colorBREAK} { .fRbuttons.buttCOLOR2 configure -fg "#000000" } else { .fRbuttons.buttCOLOR2 configure -fg "#ffffff" } } elseif {"$colorID" == "color0"} { .fRbuttons.buttCOLOR0 configure -bg $COLOR0hex set sumCOLOR0 [expr {$COLOR0r + $COLOR0g + $COLOR0b}] if {$sumCOLOR0 > $colorBREAK} { .fRbuttons.buttCOLOR0 configure -fg "#000000" } else { .fRbuttons.buttCOLOR0 configure -fg "#ffffff" } } else { ## Seems to be an invalid colorID. return } } ## END OF PROC 'update_color_buttons' ##+##################################################################### ## PROC: reset_parms ## ## PURPOSE: ## Sets values for the 3 scale variables of the GUI. ## ## CALLED BY: the 'ResetParms' button. ##+#################################################################### proc reset_parms {} { ## FOR TESTING: (dummy out this proc) # return global CHORDdegrees IMGsizePx CIRCUMpercent set CHORDdegrees 20.0 set IMGsizePx 300 set CIRCUMpercent 50.0 } ## END OF PROC 'reset_parms' ##+#################################################################### ## PROC: 'clear_canvas' ##+#################################################################### ## PURPOSE: Clears the lines and text from the canvas. ## ## CALLED BY: the 'Clear' button ##+#################################################################### proc clear_canvas {} { .fRcanvas.can delete all } ## END OF PROC 'clear_canvas' ##+##################################################################### ## PROC: 'advise_user' ##+##################################################################### ## PURPOSE: Puts a message to the user on the GUI. ## ## CALLED BY: the 'writePSfile' proc ##+##################################################################### proc advise_user {text} { .fRinfo.labelINFO1 configure -text "$text" .fRinfo.labelINFO2 configure -text "" ## Make sure the text is displayed on the GUI. update ## Alternatively, we could put the message in the title-bar ## of the GUI window. (But it is easy for the user to ## fail to see the message there. Besides, we have more ## options in displaying the message by putting it on a ## Tk widget in the GUI.) ## # wm title . "$text" } ## END OF PROC 'advise_user' ##+##################################################################### ## PROC 'show_state' ##+##################################################################### ## PURPOSE: To report on the state of the current configuration of ## the 'central' and 'circumference' angles. ## ## - Calculate the lengths of the line segments (sides) of the ## 'central' and 'circumference' angles --- using the ## 'world coordinates'. Then ... ## ## - Calculate the 'circumference' angle and the 'central' angle ## (in radians and degrees) computed using the Law of Cosines. ## ## ## CALLED BY: the 'ShowState' button ##+##################################################################### proc show_state {} { ## FOR TESTING: (dummy out this routine) # return global radsPERdeg pi CHORDdegrees CIRCUMpercent decimalDIGITS integerDIGITS ## Calculated variables from the 'ReDraw' proc. global ChordX1 ChordY1 ChordX2 ChordY2 xPeak yPeak ChordLen # CentralDegs CentralRads ################################################################### ## Calculate the lengths of the sides of the 'central' angle. ################################################################### # set lenA1 [expr {hypot($ChordX1 - 0.0,$ChordY1 - 0.0)}] set lenA1 [expr {hypot($ChordX1,$ChordY1)}] # set lenA2 [expr {hypot($ChordX2 - 0.0,$ChordY2 - 0.0)}] set lenA2 [expr {hypot($ChordX2,$ChordY2)}] ################################################################### ## Calculate the lengths of the sides of the 'circumference' angle. ################################################################### set lenC1 [expr {hypot($ChordX1 - $xPeak,$ChordY1 - $yPeak)}] set lenC2 [expr {hypot($ChordX2 - $xPeak,$ChordY2 - $yPeak)}] ################################################################### ## Calculate angles A (central) and C (circumference) using the ## Law of Cosines and the arcosine function --- where the ## Law of Cosines is of the form ## ## c^2 = a^2 + b^2 - 2*a*b*cos(C) ## so ## cos(C) = (a^2 + b^2 - c^2)/(2*a*b) ## We take the arc-cosine of the RHS to get the angle we seek. ## ## In our 2 cases, c is the chord length and a & b are the side lengths ## of the central and circumference angles. ################################################################### set cosCentAng [expr {(($lenA1 * $lenA1) + ($lenA2 * $lenA2) - ($ChordLen * $ChordLen))/(2.0*$lenA1*$lenA2)}] set CentAngRads [expr {acos($cosCentAng)}] set CentAngDegs [expr {$CentAngRads / $radsPERdeg}] set cosCircumAng [expr {(($lenC1 * $lenC1) + ($lenC2 * $lenC2) - ($ChordLen * $ChordLen))/(2.0*$lenC1*$lenC2)}] set CircumAngRads [expr {acos($cosCircumAng)}] set CircumAngDegs [expr {$CircumAngRads / $radsPERdeg}] ################################################################### ## Format various lengths for display below. ################################################################### set ChordLenFMTD [format "%${integerDIGITS}.${decimalDIGITS}f" $ChordLen] set A1lenFMTD [format "%${integerDIGITS}.${decimalDIGITS}f" $lenA1] set A2lenFMTD [format "%${integerDIGITS}.${decimalDIGITS}f" $lenA2] set C1lenFMTD [format "%${integerDIGITS}.${decimalDIGITS}f" $lenC1] set C2lenFMTD [format "%${integerDIGITS}.${decimalDIGITS}f" $lenC2] ############################################ ## Create the heading for the state listing. ############################################ set VARlist \ "Given a Circle Chord: The Subtending Angle At The Circle Center Is Twice Any Subtending Angle At The Circumference. STATE OF THE CURRENT CONFIGURATION OF A 'CENTRAL' ANGLE AND A 'CIRCUMFERENCE' ANGLE (verification of this theorem for the current configuration): -------------------------------------------------- Current Coordinates of the end-points of the chord --- in a circle of unit radius: -------------------------------------------------- ChordX1,ChordY1: [format "%${integerDIGITS}.${decimalDIGITS}f" $ChordX1] , [format "%${integerDIGITS}.${decimalDIGITS}f" $ChordY1] ChordX2,ChordY2: [format "%${integerDIGITS}.${decimalDIGITS}f" $ChordX2] , [format "%${integerDIGITS}.${decimalDIGITS}f" $ChordY2] ------------------------------------------------------------------- Current Coordinates of the center point A and circumference point C: ------------------------------------------------------------------- Ax,Ay: 0.0 , 0.0 Cx,Cy: [format "%${integerDIGITS}.${decimalDIGITS}f" $xPeak] , [format "%${integerDIGITS}.${decimalDIGITS}f" $yPeak] --------------------------------------------- Lengths calculated by the Pythagorean Theorem: --------------------------------------------- Length of the chord: $ChordLenFMTD Length of the 2 sides of the central angle at A: $A1lenFMTD $A2lenFMTD Length of the 2 sides of the circumference angle at C: $C1lenFMTD $C2lenFMTD ------------------------------ Computed by the Law of Cosines: ------------------------------ Cosine of angle A: ( (A1 * A1) + (A2 * A2) - (ChordLen * ChordLen) ) / (2 * A1 * A2) = ( ($A1lenFMTD * $A1lenFMTD) + ($A2lenFMTD * $A2lenFMTD) - ($ChordLenFMTD * $ChordLenFMTD) ) / (2.0 * $A1lenFMTD * $A2lenFMTD) = [format "%${integerDIGITS}.${decimalDIGITS}f" $cosCentAng] Cosine of angle C: ( (C1 * C1) + (C2 * C2) - (ChordLen * ChordLen) ) / (2 * C1 * C2) = ( ($C1lenFMTD * $C1lenFMTD) + ($C2lenFMTD * $C2lenFMTD) - ($ChordLenFMTD * $ChordLenFMTD) ) / (2.0 * $C1lenFMTD * $C2lenFMTD) = [format "%${integerDIGITS}.${decimalDIGITS}f" $cosCircumAng] ----------------------------- Using the arc-cosine function: ----------------------------- Angle A: [format "%${integerDIGITS}.${decimalDIGITS}f" $CentAngRads] radians or [format "%${integerDIGITS}.${decimalDIGITS}f" $CentAngDegs] degrees Angle C: [format "%${integerDIGITS}.${decimalDIGITS}f" $CircumAngRads] radians or [format "%${integerDIGITS}.${decimalDIGITS}f" $CircumAngDegs] degrees NOTE: The angle at A (the 'central' angle) is always twice the angle at C (the 'circumference' angle). " ############################## ## Show the list. ############################## popup_msgVarWithScroll .topList "$VARlist" +20+30 } ## END OF proc 'show_state' ##+######################################################################## ## PROC: 'writePSfile' ##+######################################################################## ## PURPOSE: Write a Postscript file from the image on the canvas. ## ## CALLED BY: the 'WritePSfile' button ##+######################################################################## proc writePSfile {} { global DIRtemp env PSviewer IMGsizePx ########################################################## ## Set a name for the output file and write the Postscript ## file from the canvas contents. ########################################################## set OUTfile "$DIRtemp/$env(USER)_InscribedAngleTheorem.ps" ## FOR TESTING: if {0} { puts "" puts "PROC 'writePSfile':" puts " OUTfile: $OUTfile" } set err [.fRcanvas.can postscript -file "$OUTfile" \ -colormode color \ -width $IMGsizePx \ -height $IMGsizePx \ -x 0 -y 0 \ -rotate false \ -pageheight 11.0i \ -pagewidth 8.5i \ -pagey 0.5i \ -pagex 0.5i \ -pageanchor sw] ## Tk canvas 'postscript' option NOTES: ## ## Color modes: color, gray, mono ## ## It is important to use the '-width' and -height' parms to ## give the size of the image in pixels --- otherwise the ## image may be clipped or positioned improperly on the 'page'. if {"$err" == ""} { advise_user \ "** Postscript file was written to file $OUTfile ** Started viewer $PSviewer **" exec $PSviewer "$OUTfile" & } else { advise_user \ "** Postscript write may have FAILED. Message: $err **" } } ## END OF PROC 'writePSfile' ##+######################################################################## ## PROC: 'popup_msgVarWithScroll' ##+######################################################################## ## PURPOSE: Report help or error conditions to the user. ## ## We do not use focus,grab,tkwait in this proc, ## because we use it to show help when the GUI is idle, ## and we may want the user to be able to keep the Help ## window open while doing some other things with the GUI ## such as putting a filename in the filename entry field ## or clicking on a radiobutton. ## ## For a similar proc with focus-grab-tkwait added, ## see the proc 'popup_msgVarWithScroll_wait' in a ## 3DterrainGeneratorExaminer Tk script. ## ## REFERENCE: page 602 of 'Practical Programming in Tcl and Tk', ## 4th edition, by Welch, Jones, Hobbs. ## ## ARGUMENTS: A toplevel frame name (such as .fRhelp or .fRerrmsg) ## and a variable holding text (many lines, if needed). ## ## CALLED BY: 'help' button ##+######################################################################## ## To have more control over the formatting of the message (esp. ## words per line), we use this 'toplevel-text' method, ## rather than the 'tk_dialog' method -- like on page 574 of the book ## by Hattie Schroeder & Mike Doyel,'Interactive Web Applications ## with Tcl/Tk', Appendix A "ED, the Tcl Code Editor". ##+######################################################################## proc popup_msgVarWithScroll { toplevName VARtext ULloc} { ## global fontTEMP_varwidth #; Not needed. 'wish' makes this global. ## global env # bell # bell ################################################# ## Set VARwidth & VARheight from $VARtext. ################################################# ## To get VARheight, ## split at '\n' (newlines) and count 'lines'. ################################################# set VARlist [ split $VARtext "\n" ] ## For testing: # puts "VARlist: $VARlist" set VARheight [ llength $VARlist ] ## For testing: # puts "VARheight: $VARheight" ################################################# ## To get VARwidth, ## loop through the 'lines' getting length ## of each; save max. ################################################# set VARwidth 0 ############################################# ## LOOK AT EACH LINE IN THE LIST. ############################################# foreach line $VARlist { ############################################# ## Get the length of the line. ############################################# set LINEwidth [ string length $line ] if { $LINEwidth > $VARwidth } { set VARwidth $LINEwidth } } ## END OF foreach line $VARlist ## For testing: # puts "VARwidth: $VARwidth" ############################################################### ## NOTE: VARwidth works for a fixed-width font used for the ## text widget ... BUT the programmer may need to be ## careful that the contents of VARtext are all ## countable characters by the 'string length' command. ############################################################### ##################################### ## SETUP 'TOP LEVEL' HELP WINDOW. ##################################### catch {destroy $toplevName} toplevel $toplevName # wm geometry $toplevName 600x400+100+50 # wm geometry $toplevName +100+50 wm geometry $toplevName $ULloc wm title $toplevName "Note" # wm title $toplevName "Note to $env(USER)" wm iconname $toplevName "Note" ##################################### ## In the frame '$toplevName' - ## DEFINE THE TEXT WIDGET and ## its two scrollbars --- and ## DEFINE an OK BUTTON widget. ##################################### if {$VARheight > 10} { text $toplevName.text \ -wrap none \ -font fontTEMP_fixedwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 \ -yscrollcommand "$toplevName.scrolly set" \ -xscrollcommand "$toplevName.scrollx set" ## -font fontTEMP_varwidth scrollbar $toplevName.scrolly \ -orient vertical \ -command "$toplevName.text yview" scrollbar $toplevName.scrollx \ -orient horizontal \ -command "$toplevName.text xview" } else { text $toplevName.text \ -wrap none \ -font fontTEMP_fixedwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 ## -font fontTEMP_varwidth } button $toplevName.butt \ -text "OK" \ -font fontTEMP_varwidth \ -command "destroy $toplevName" ############################################### ## PACK *ALL* the widgets in frame '$toplevName'. ############################################### ## Pack the bottom button BEFORE the ## bottom x-scrollbar widget, pack $toplevName.butt \ -side bottom \ -anchor center \ -fill none \ -expand 0 if {$VARheight > 10} { ## Pack the scrollbars BEFORE the text widget, ## so that the text does not monopolize the space. pack $toplevName.scrolly \ -side right \ -anchor center \ -fill y \ -expand 0 ## DO NOT USE '-expand 1' HERE on the Y-scrollbar. ## THAT ALLOWS Y-SCROLLBAR TO EXPAND AND PUTS ## BLANK SPACE BETWEEN Y-SCROLLBAR & THE TEXT AREA. pack $toplevName.scrollx \ -side bottom \ -anchor center \ -fill x \ -expand 0 ## DO NOT USE '-expand 1' HERE on the X-scrollbar. ## THAT KEEPS THE TEXT AREA FROM EXPANDING. pack $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } else { pack $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } ################################################ ## Set some 'event' bindings to allow for ## easy scrolling through huge listings. ## is a press of the Page-Down key. ## is a press of the Page-Up key. ## is a press of the Home key ## to go to the top of the listing. ## is a press of the End key ## to go to the bottom of the listing. ## is a press of the Up-arrow key. ## is a press of the Down-arrow key. ################################################ bind $toplevName "$toplevName.text yview scroll +1 page" bind $toplevName "$toplevName.text yview scroll -1 page" bind $toplevName "$toplevName.text see 1.0" bind $toplevName "$toplevName.text see end" bind $toplevName "$toplevName.text yview scroll -1 unit" bind $toplevName "$toplevName.text yview scroll +1 unit" ##################################### ## LOAD MSG INTO TEXT WIDGET. ##################################### ## $toplevName.text delete 1.0 end $toplevName.text insert end $VARtext $toplevName.text configure -state disabled } ## END OF PROC 'popup_msgVarWithScroll' ##+######################## ## END of PROC definitions. ##+######################## ## Set HELPtext var. ##+######################## set HELPtext \ " ** HELP for this App to Demonstrate the 'Inscribed Angle Theorem' ** This Tk script is meant to demonstrate the theorem that says: Given a chord in a circle, consider two angles subtending the chord --- one angle with vertex at the center of the circle and the other angle with vertex on the circumference of the circle (and on the same side of the chord as the center of the circle). Then the 'central angle' is EXACTLY TWICE the size of the 'circumference angle'. For brevity, this theorem is sometimes called the 'inscribed angle' theorem --- or the 'central angle' theorem. (Note that for a given chord, there is ONE 'central angle' but there can be an INFINITE number of 'circumference angles'. The theorem says that no matter the position of the vertex of the 'circumference angle' on the arc of the circle that 'contains' the central angle, the 'circumference angle' is ALWAYS half the size of the 'central angle'.) NOTE: We are NOT using this GUI to show a proof of the theorem. We use the GUI to VERIFY that the central angle is always twice the inscribed angle --- no matter how we position the chord and how we position the point C. You can consult the following references for proofs (with diagrams). ********** REFERENCES: ********** The first half of Chapter 6 of the book 'Trigonometric Delights' by Eli Maor, 1998, gives a nice presentation of this 'inscribed angle' theorem. Maor points out that there are several truths that follow rather quickly from this theorem --- including Thales' theorem (that every triangle inscribed in a semicircle, with one side on the diameter of the semicircle, is a right triangle) and the sine and cosine double angle and half angle formulas. Also see the web page at https://en.wikipedia.org/wiki/Inscribed_angle ******************** OPERATION OF THE GUI: ******************** This GUI uses a square Tk 'canvas' widget to show the circle and and a chord inscribed within it. 'Without loss of generality', we can always draw the chord horizontally (parallel to the bottom and top sides of the square Tk 'canvas' widget). A 'scale' widget on the GUI allows the user to easily draw the chord. The scale widget allows the user to specify an angle, A, between 0 and 180 degrees. The chord is drawn connecting two points on the circle at angles (270 - A) and (270 + A) degrees --- where we are assuming the usual configuration of a positive x-axis pointing to the right and a positive y-axis pointing upward, and the angle 270 degrees being measured counter-clockwise from the positive x-axis. Note that when A is zero, the end points of the chord are two coinciding points at 270 degrees on the circle --- and when A is 90 degrees, the end points of the chord are at 180 and 3600 degrees on the circle, and the chord is the diameter of the circle. When A is 45 degrees, the end points of the chord are at 235 and 315 degrees on the circle, and the chord is in the bottom half of the circle. (The Tk 'create oval' command is used to draw the circle on the Tk 'canvas' widget. A Tk 'create line' command is used to draw the chord.) The Tk 'create line' command can then be used to draw the two sides of the 'central angle' --- by drawing the 2 line segments that connect the center of the circle to the 2 endpoints of the chord. A second 'scale' widget on the GUI allows the user to easily specify the point location (on the arc of the circle that 'contains' the 'central angle') of the vertex of the 'circumference angle'. Three 'Color' buttons on the GUI allow the user to bring up an external Tk color-selector script to specify 1) a background (canvas) color and 2) a color for the lines (the circle, the chord, and the angle sides) 3) text drawn on the canvas. A 'ReDraw' button on the GUI allows the user to redraw the the image if the user uses the 3 'scale' widgets or the 3 'Color' button widgets to change the image to be displayed. ********************** WINDOW or IMAGE RESIZE: ********************** We allow the user to resize the window rather than using a fixed window (and fixed drawing) size. If the user resizes the window, the 'ImageSize' 'scale' widget can be used to request a larger or smaller image size. Then the 'ReDraw' button can be used to force the drawing to be resized according to the requested image size. *************************************** USING THE SLIDER ON THE 'SCALE' WIDGETS: *************************************** You can click MB1 (mouse-button-1) on the 'slider' of a 'scale' widget and drag the slider. When you get the settings of the 'scale' widgets where you want them, click the 'ReDraw' button to redraw the image (chord and 2 angles in the circle). If you want FINE CONTROL of moving the slider, there are a couple of other options based on clicking in the trough on either side of the slider: 1) Click-and-release on either side of the slider, to change the figure by one increment of the scale per click-release. OR 2) Click-and-hold on either side of the slider. The slider moves until you release MB1. As the slider goes from 0 to 180, the 'chord' is drawn, horizontally, at the bottom of the circle going to the top of the circle. *************************** ADDITIONAL ITEMS DISPLAYED: *************************** The size of the 'central' and 'circumference' angles (in degrees and radians) are shown in a message area on the GUI --- for each 'ReDraw'. In addition, various numeric properties of the chord and angles could be displayed as the chord and 'circumference point' are moved along the circumference of the circle and ReDraws are done --- numeric properties such as - the length of the chord (we assume a radius 1.0 of the circle --- so the chord length can vary between 0.0 and 2.0), - the lengths of the 2 sides of the 'circumference angle', - the area of the triangle created by the chord and the 'central angle', - the area of the triangle created by the chord and the 'circumference angle', - and still more. *********************** CAPTURING THE GUI IMAGE: A screen/window capture utility (like 'gnome-screenshot' on Linux) can be used to capture the GUI image in a PNG or GIF file, say. If necessary, an image editor (like 'mtpaint' on Linux) can be used to crop the window capture image. The image could also be down-sized --- say to make a smaller image suitable for use in a web page or an email. Alternatively, a 'WritePSfile' button on the GUI can be used to write a Postscript file of the image drawn on the Tk canvas. This works when the image is so large (in the scrollable canvas) that part of the image does not fit on the monitor screen. " ##+################################################################ ##+################################################################ ## ADDITIONAL GUI INITIALIZATION SECTION: Mainly to ## - Draw an initial triangle and semicircle on the canvas. ##+################################################################ ##+################################################################ ##+##################################################### ## Set the full-name of the RGB color-selector Tk script ## that is used in several procs above. ##+##################################################### ## FOR TESTING: # puts "argv0: $argv0" set DIRthisScript "[file dirname $argv0]" ## For ease of testing in a Linux/Unix terminal and located at the ## directory containing this Tk script. Set the full directory name. if {"$DIRthisScript" == "."} { set DIRthisScript "[pwd]" set CURdir "[pwd]" } set DIRupOne "[file dirname "$DIRthisScript"]" set DIRupTwo "[file dirname "$DIRupOne"]" set ColorSelectorScript "$DIRupTwo/SELECTORtools/tkRGBselector/sho_colorvals_via_sliders3rgb.tk" ## Alternatively: Put the RGB color-selector Tk script in the ## same directory as this Tk script and uncomment the following. # set ColorSelectorScript "$DIRthisScript/sho_colorvals_via_sliders3rgb.tk" ##+############################################# ## Set an output directory for Postscript files. ##+############################################# set DIRtemp "/tmp" ##+############################################# ## Initialize the canvas-background-color. ## ## (Change 'if {1}' to 'if {0}' to try an ## alternative min-color.) ##+############################################# if {1} { ## Red: set COLOR0r 255 set COLOR0g 0 set COLOR0b 0 } else { ## White: set COLOR0r 255 set COLOR0g 255 set COLOR0b 255 } set COLOR0hex [format "#%02X%02X%02X" $COLOR0r $COLOR0g $COLOR0b] update_color_buttons "color0" ##+############################################# ## Initialize the canvas-line-segments-color. ## ## (Change 'if {1}' to 'if {0}' to try an ## alternative canvas-line-segment-color.) ##+############################################# if {1} { ## Black: set COLOR1r 0 set COLOR1g 0 set COLOR1b 0 } else { ## White: set COLOR1r 255 set COLOR1g 255 set COLOR1b 255 } set COLOR1hex [format "#%02X%02X%02X" $COLOR1r $COLOR1g $COLOR1b] update_color_buttons "color1" ##+############################################# ## Initialize the canvas-text-color. ## ## (Change 'if {1}' to 'if {0}' to try an ## alternative canvas-text-color.) ##+############################################# if {1} { ## Orange: set COLOR2r 255 set COLOR2g 200 set COLOR2b 0 } else { ## Yellow: set COLOR2r 255 set COLOR2g 255 set COLOR2b 0 } set COLOR2hex [format "#%02X%02X%02X" $COLOR2r $COLOR2g $COLOR2b] update_color_buttons "color2" ##+################################################### ## Set the initial values of the 'scale' widgets. ##+################################################### reset_parms ##+###################################################### ## Set a 'margin-percent' to use for placing the drawing ## within the 4 edges of the canvas. ## (NOT USED, yet) ##+###################################################### set marginPercent 10 # set marginPercent 5 ##+################################################ ## Set a width for lines (including arcs). ##+################################################ # set lineWidthPx 1 set lineWidthPx 2 ##+################################################# ## Set constants to use for angle conversions --- ## degrees to radians. ##+################################################ set pi [expr {4.0 * atan(1.0)}] set radsPERdeg [expr {$pi/180.0}] ##+################################################## ## Set number of decimal-digits and 'integer-digits' ## to show in displaying lengths and angles. ##+################################################## # set decimalDIGITS 4 set decimalDIGITS 3 # set integerDIGITS 4 set integerDIGITS 3 #+####################################################### ## Set a viewer for Postscript files. #+####################################################### # set PSviewer "/usr/bin/display" # set PSviewer "/usr/bin/psview" # set PSviewer "/usr/bin/gsview" # set PSviewer "/usr/bin/gs -q -dBATCH" # set PSviewer "/usr/bin/xpdf" set PSviewer "/usr/bin/evince" ##+################################################# ## Draw the circle and two angles, for the current ## requested image size. ##+################################################# ReDraw