Updated 2012-11-30 18:00:05 by pooryorick

Arjen Markus (30 december 2002) I developed the script that follows as an experiment in educational software. My experiences with such software are very limited, so I pretend nothing about its usefulness. It just struck me that classical geometrical constructions, like bisecting an angle or drawing a regular hexagon can be shown in a simple animation to make the concept more lifely.

In order to do that, I needed a small drawing language. It is a kind of "turtle geometry" (if I understand that term correctly). I keep track of a current position and draw from there. The aspects that make this language easy to use are:

• The commands are short
• You can draw in cartesian and polar coordinates
• You have a limited set of attributes to worry about
• By using relative drawing commands, you do not have to calculate the coordinates - just let the script worry about them.

There are two examples: constructing a hexagon and working with fractions (I remember the nice display from my own school days).

AM (24 june 2003) I have started to improve the script below, with the suggestions from Peter Milne (notably the bounding box) and a new mode, "turtle", with accompanying commands modelled after LOGO's turtle graphics.

The thing is becoming rather lengthy (mostly because of the comments, mind you, that head each proc), so probably the best way to distribute it, is as a starkit.

For yet another example of what you can do with this little package: Daddy, how does a computer work

AM (8 july 2003) Submitted the application as a starkit, with four different demos to choose from - including the fractions demo by Peter (slightly adjusted to fit the default screen). The starkit is called "plain_geometry", to emphasize that it deals with simple geometry in the (Euclidean) plane.

See the sdarchive for the starkit.

AM (12 january 2005) This approach still makes it necessary to do all kinds of coordinate computations. I wanted to avoid that - it is tedious and error-prone. So, here is a quick alternative - Drawing geometrical objects
```# constructions.tcl --
#
#    Package providing tools for showing geometrical constructions
#
# Version information:
#    version 0.1: initial implementation, december 2002
#

# Constructions --
#    namespace to hold all specific variables and procedures
#

package require Tk

namespace eval ::Constructions {
variable mode   "cartesian"
variable canvas     .c
variable colour     black
variable fillcolour black
variable textcolour black
variable textfont   "Times 10"
variable delay   300
variable xcurr     0.0
variable ycurr     0.0
variable width    12.0
variable height   12.0
variable xmin
variable xmax
variable ymin
variable ymax

namespace export draw display moveto colour mode \
textfont textcolour erase
}

# mode --
#    Set the coordinates mode (cartesian or polar)
#
# Arguments:
#    type         New mode
#
# Result:
#    None
#
# Side effect:
#    The interpretation of coordinate arguments is changed, if the
#    type is a valid type. Otherwise it is left unchanged
#
proc ::Constructions::mode {type} {
variable mode

if { \$type == "cartesian" || \$type == "polar" } {
set mode \$type
}
}

# textcolour --
#    Set the colour for text
#
# Arguments:
#    newcolour     New colour to use
#
# Result:
#    None
#
# Side effect:
#    Set a new colour for subsequent text drawing actions
#
proc ::Constructions::textcolour {newcolour} {
variable textcolour

set textcolour \$newcolour
}

# textfont --
#    Set the font for text
#
# Arguments:
#    newfont       New font to use
#
# Result:
#    None
#
# Side effect:
#    Set a new font for subsequent text drawing actions
#
proc ::Constructions::textfont {newfont} {
variable textfont

set textfont \$newfont
}

# colour --
#    Set the current colour
#
# Arguments:
#    newcolour    New colour to be used for outlines
#    newfill      New colour to be used for filling (defaults to newcolour)
#
# Result:
#    None
#
# Side effect:
#    Set a new colour for subsequent drawing actions
#
proc ::Constructions::colour {newcolour {newfill "same"}} {
variable colour
variable fillcolour

set colour \$newcolour

if { \$newfill == "same" } {
set fillcolour \$newcolour
} else {
set fillcolour \$newfill
}
}

# moveto --
#    Set the current coordinates
#
# Arguments:
#    newx         New x coordinate or distance from origin
#    newy         New y coordinate or angle to positive x-axis
#
# Result:
#    None
#
# Side effect:
#    Set a new "current" position for subsequent drawing actions
#
proc ::Constructions::moveto {newx newy} {
variable mode
variable xcurr
variable ycurr

if { \$mode == "cartesian" } {
set xcurr \$newx
set ycurr \$newy
} else {
set dist  \$newx
set angle \$newy

}
}

# moverel --
#    Move the current coordinates by the given vector
#
# Arguments:
#    delx         X coordinate of vector over which to move or distance
#    dely         Y coordinate or angle
#
# Result:
#    None
#
# Side effect:
#    Set a new "current" position for subsequent drawing actions
#
proc ::Constructions::moverel {delx dely} {
variable xcurr
variable ycurr

if { \$mode == "cartesian" } {
set xcurr [expr {\$xcurr+\$delx}]
set ycurr [expr {\$ycurr+\$dely}]
} else {
set dist  \$delx
set angle \$dely

}
}

# erase --
#    Erase items from the canvas
#
# Arguments:
#    tagorid       Tag or ID of item(s) to erase
#
# Result:
#    None
#
# Side effect:
#    Removes items from the canvas
#
proc ::Constructions::erase {tagorid} {
variable canvas

\$canvas delete \$tagorid
}

# draw --
#    Draw an object into the canvas
#
# Arguments:
#    objtype      Type of object
#    args         List of arguments, appropriate for type
#
# Result:
#    ID of object that was created (or a specific tag)
#
proc ::Constructions::draw {objtype args} {
variable mode
variable xcurr
variable ycurr
variable xmin
variable xmax
variable ymin
variable ymax
variable canvas
variable colour
variable fillcolour
variable textcolour
variable textfont
variable delay

variable go_on

switch -- \$objtype {
"grid" {
for { set x \$xmin } { \$x < \$xmax } { set x [expr {\$x+1.0}] } {
\$canvas create line \${x}c \${ymin}c \${x}c \${ymax}c -tag grid -fill gray
}
for { set y \$ymin } { \$y < \$ymax } { set y [expr {\$y+1.0}] } {
\$canvas create line \${xmin}c \${y}c \${xmax}c \${y}c -tag grid -fill gray
}
\$canvas move grid \${xmax}c \${ymax}c
return grid
}
"axes" {
\$canvas create line \${xmin}c 0.0c \${xmax}c 0.0c -tag axes -fill black
\$canvas create line 0.0c \${ymin}c 0.0c \${ymax}c -tag axes -fill black
\$canvas move axes \${xmax}c \${ymax}c
return axes
}
"line" {
if { \$mode == "cartesian" } {
set xp     [lindex \$args 0]
set yp     [lindex \$args 1]
set xcurr  [lindex \$args 2]
set ycurr  [lindex \$args 3]
} else {
set dist1  [lindex \$args 0]
set angle1 [lindex \$args 1]
set dist2  [lindex \$args 2]
set angle2 [lindex \$args 3]
}
set x1    "\${xp}c"
set y1    "[expr {-\$yp}]c"
set x2    "\${xcurr}c"
set y2    "[expr {-\$ycurr}]c"

set obj [\
\$canvas create line \$x1 \$y1 \$x2 \$y2 -fill \$colour]
}
"linerel" {
set x1 "\${xcurr}c"
set y1 "[expr {-\$ycurr}]c"
if { \$mode == "cartesian" } {
set xcurr [lindex \$args 0]
set ycurr [lindex \$args 1]
} else {
set dist  [lindex \$args 0]
set angle [lindex \$args 1]

}
set x2    "\${xcurr}c"
set y2    "[expr {-\$ycurr}]c"

set obj [\
\$canvas create line \$x1 \$y1 \$x2 \$y2 -fill \$colour]
}
"circle" -
"disc"   {

if { \$objtype == "circle" } {
set fill {}
} else {
set fill \$fillcolour
}

set obj [\
\$canvas create oval \$x1 \$y1 \$x2 \$y2 -outline \$colour -fill \$fill]
}
"arc" -
"pie" {
set start [lindex \$args 1]
set stop  [lindex \$args 2]

if { \$objtype == "arc" } {
set fill {}
set style arc
} else {
set fill \$fillcolour
set style pie
}

set obj [\
\$canvas create arc \$x1 \$y1 \$x2 \$y2 -outline \$colour \
-start \$start -extent [expr {\$stop-\$start}] \
-style \$style -fill \$fill]
}

"text" {
set x1    "\${xcurr}c"
set y1    "[expr {-\$ycurr}]c"
set text  [lindex \$args 0]

set obj [\
\$canvas create text \$x1 \$y1 -text \$text -fill \$textcolour \
-font \$textfont]
}

default {return {}}
}

#
# Move the newly created object to the centre of the window,
# that is, correct for the origin
#
\$canvas move \$obj \${xmax}c \${ymax}c

#
# Wait a while before returning - gives a nice animated effect
#
set go_on 0
after \$delay {set ::Constructions::go_on 1}
vwait ::Constructions::go_on
return \$obj
}

# display --
#    Create the initial canvas
#
# Arguments:
#    None
# Result:
#    None
#
proc ::Constructions::display {} {
variable canvas
variable xmin
variable xmax
variable ymin
variable ymax
variable width
variable height

canvas \$canvas -background white -width \${width}c -height \${height}c
pack   \$canvas -fill both

set xmin [expr {-\$width/2.0}]
set xmax [expr {+\$width/2.0}]
set ymin [expr {-\$height/2.0}]
set ymax [expr {+\$height/2.0}]

draw grid
draw axes
}```
```# main --
#    Main code
#
namespace import ::Constructions::*

if { 1 } {
display
moveto       0.0    5.5
textfont "Times 14"
draw text "Construct a hexagon"
#
# Reset the drawing position - all is relative
#
moveto       0.0    0.0
mode   "polar"
colour "black"
draw circle 5.0
colour "red"
moveto       5.0    90
draw disc    0.1

draw arc     5.0   -90 -20
draw linerel 5.0   -30
draw disc    0.1

draw arc     5.0   -70 -100
draw linerel 5.0   -90
draw disc    0.1

draw arc     5.0  -130 -160
draw linerel 5.0  -150
draw disc    0.1

draw arc     5.0  -200 -223
draw linerel 5.0  -210
draw disc    0.1

draw arc     5.0  -263 -289
draw linerel 5.0  -270
draw disc    0.1

draw arc     5.0  -310 -335
draw linerel 5.0  -330
draw disc    0.1
}

if { 0 } {
mode   "cartesian"
colour "black" "blue"
textcolour "black"
textfont   "Times 20 bold"
display
erase axes
erase grid
moveto      -3.5  3.5
draw pie     1.8  90.1 270    ;# Ugly drawing under Windows 98
moveto      -4.0  0
draw text    "1/2"

moveto      -2.5  3.5
draw text    "+"
moveto      -2.5  0
draw text    "+"

moveto       0.5  3.5
draw pie     1.8  90.1 270
moveto       0.0  0
draw text    "1/2"

moveto       1.0  3.5
draw text    "="
moveto       1.0  0
draw text    "="

moveto       4.0  3.5
draw disc    1.8
moveto       4.0  0
draw text    "1"
}```

Peter Milne [email protected] ( I hope you don't mind direct updates :-) ) It is a useful little toolkit, easy to write extended applications - I tried a short extension of the fractions picture. Biggest problem was maintaining the x-cursor - maybe the canvas can return its bounding rectangle to make this easier?

AM Please, updating is part of the Wiki philosophy, and it shows that people read and use these pages. (I corrected the formatting a bit). Thanks for the feedback. I will try and see what can be done.
```#
# Example fraction teaching app (Peter Milne [email protected])
#

# pie --
#    Convenience proc to draw a pie
#
# Arguments:
#    x y        origin
#    t1 t2        start, end angles (normalised for easy math, display)
#    label
#    fillcolour
#
# Result:
#    Updated x cursor
#    WORKTODO: x update is a kludge, large on lh pies, small on rh pies
#
# Side effect:
#    Set a new colour for subsequent drawing actions
#
proc pie {x y t1 t2 label fillcolour} {
moveto \$x \$y
colour "black" \$fillcolour
draw pie 1.8 [expr \$t1+90] [expr \$t2+90]
moveto \$x [expr \$y-3.5 ]
draw text \$label
return [expr \$x+2]
}

# label --
#    Convenience proc to draw a label
#
# Arguments:
#    x y        origin
#    label
#
# Result:
#    Updated x cursor
#
proc label {x y label} {
moveto \$x \$y
draw text \$label
return [expr \$x+2.5]
}

if { 1 } {
mode   "cartesian"
textcolour "black"
textfont   "Times 20 bold"
display
erase axes
erase grid
set x -3.5; set y 7

set x [pie \$x \$y 0 180 "1/2" "blue"]
set x [label \$x \$y "+"]
set x [pie \$x \$y 180 360 "1/2" "yellow"]
set x [label \$x \$y "="]
pie \$x \$y 0 180 "1" "blue"
pie \$x \$y 180 360 "1" "yellow"

set x -3.5; set y 0
set x [pie \$x \$y 0 120 "1/3" "blue"]
set x [label \$x \$y "+"]
set x [pie \$x \$y 120 240 "1/3" "yellow"]
set x [label \$x \$y "+"]
set x [pie \$x \$y 240 360 "1/3" "red"]
set x [label \$x \$y "="]
pie \$x \$y 0 120   "1" "blue"
pie \$x \$y 120 240 "1" "yellow"
pie \$x \$y 240 360 "1" "red"

set x -3.5; set y -7

set x [pie \$x \$y 0 90 "1/4" "blue"]
set x [label \$x \$y "+"]
set x [pie \$x \$y 90 180 "1/4" "yellow"]
set x [label \$x \$y "+"]
set x [pie \$x \$y 180 270 "1/4" "red"]
set x [label \$x \$y "+"]
set x [pie \$x \$y 270 360 "1/4" "green"]
set x [label \$x \$y "="]
pie \$x \$y 0 90 "1" "blue"
pie \$x \$y 90 180 "1" "yellow"
pie \$x \$y 180 270 "1" "red"
pie \$x \$y 270 360 "1" "green"
}```