#!/bin/sh
# The next line is executed by /bin/sh, but not Tcl \
exec wish $0 ${1+"$@"}
##+##########################################################################
#
# Octabug
#
# Animates the morphing of a octahedron into an open cuboctahedron.
# by Keith Vetter
#
# Revisions:
# KPV Mar 07, 1995 - initial revision
# KPV Jun 07, 2002 - some minor code clean up
#
##+##########################################################################
#
# do_display
#
# Sets up the display
#
proc do_display {} {
wm title . "Octabug"
canvas .c -relief raised -borderwidth 4
pack .c -side top
.c config -height 600 -width 600
xyz .eye "Eye Position" eye_ {5 4 3}
frame .buttons
button .anim -text Animate -command { set go [expr 1 - $go]; animate}
button .qbtn -text Quit -command exit
pack .buttons -side left -expand yes -fill both
pack .anim .qbtn -side top -expand yes -in .buttons
}
##+##########################################################################
#
# animate
#
# Sets things in motion
#
proc animate {} {
global go param
if $go {
set param [expr ($param + 1) % 100]
triag
after 1 animate
}
}
##+##########################################################################
#
# Triag
#
# Draws all 8 triangles of the octabug.
#
proc triag {} {
global mem param
set t $param
set t [expr $t*2.0/100] ;# Change to 0-2 range
set t1 $t ;# Remember
if {$t > 1} { set t [expr 2.0 - $t] } ;# Exploit symmetry
set t [expr $t + 1.0] ;# 1.0-2.0 range
.c delete poly
if [info exists mem($t1,a)] { ;# Did we memoize entry already?
set a $mem($t1,a)
set b $mem($t1,b)
} else { ;# Nope, recompute
set d [expr sqrt(12 - 3 * $t * $t)]
set a [expr (3*$t + $d) / 6]
set b [expr $t - $a]
if {$t1 > 1} { ;# In or out?
set d $a ; set a $b ; set b $d
}
set mem($t1,a) $a ;# Memoize--faster on next loop
set mem($t1,b) $b
}
triag2 $a $b 7 -1 -1 -1 ;# Draw all the triangles...
triag2 $a $b 6 -1 1 -1 ;# ...back to front if we can
triag2 $a $b 5 1 -1 -1
triag2 $a $b 4 1 1 -1
triag2 $a $b 3 -1 -1 1
triag2 $a $b 2 -1 1 1
triag2 $a $b 1 1 -1 1
triag2 $a $b 0 1 1 1
update
}
##+##########################################################################
#
# Triag2
#
# Draws an individual triangle
#
proc triag2 {a b color x y z} {
global colors
set color [lindex $colors $color]
set p1 [3d_obj2screen 0 [expr $y*$a] [expr $z*$b]]
set p2 [3d_obj2screen [expr $x*$b] 0 [expr $z*$a]]
set p3 [3d_obj2screen [expr $x*$a] [expr $y*$b] 0 ]
eval .c create polygon $p1 $p2 $p3 -fill $color -tags poly
}
##+##########################################################################
#
# 3d Canvas
#
# Simple 3d canvas package. After specifying the eye, the page size and a
# few other variables, this package will draw points and lines in 3d space.
#
# This is very simple. No clipping, z-buffering, or rotation is provided.
#
# Procedures:
# 3d_init
# Generates the transformation matrix needed to map from world to screen.
# Must be called after setting or changing the eye, etc.
# 3d_obj2screen
# Converts x,y,z of world coordinates into x,y of screen coordinates
#
# Variables:
# 3d(ex) 3d(ey) 3d(ez) == eye position
# 3d(rx) 3d(ry) 3d(rz) == reference point
# 3d(x) 3d(y) == canvas size
# 3d(cx) 3d(cy) == viewport center (reference point goes here)
# 3d(sx) 3d(sy) == size of viewport
#
set 3d(ex) 5 ;# Eye position
set 3d(ey) 4
set 3d(ez) 3
set 3d(rx) 0 ;# Reference point
set 3d(ry) 0
set 3d(rz) 0
set 3d(x) 600 ;# Page size
set 3d(y) 600
set 3d(cx) [expr $3d(x) / 2.0] ;# Mid-point
set 3d(cy) [expr $3d(y) / 2.0]
set 3d(sx) [expr $3d(cx) - 5.0] ;# Viewport size
set 3d(sy) [expr $3d(cy) - 6.0]
##+##########################################################################
#
# 3d_init
#
# Computes the transformation matrix for the current eye and center.
# Note, calling this resets all scaling, translations, etc.
#
proc 3d_init {} {
global 3d_mat 3d
if {$3d(ex) == 0 && $3d(ey) == 0} { set 3d(ey) .01 }
set xy [expr sqrt($3d(ex)*$3d(ex) + $3d(ey)*$3d(ey))]
set xyz [expr sqrt($xy*$xy + $3d(ez)*$3d(ez))]
3d_ident 3d_mat
3d_ident t ;# T0 - center to origin
set t(3,0) [expr -$3d(rx)]
set t(3,1) [expr -$3d(ry)]
set t(3,2) [expr -$3d(rz)]
3d_m44 3d_mat t 3d_mat
3d_ident t ;# T1 -- Origin To Eye
set t(3,0) [expr -$3d(ex)]
set t(3,1) [expr -$3d(ey)]
set t(3,2) [expr -$3d(ez)]
3d_m44 3d_mat t 3d_mat
3d_ident t ;# T2 -- Rotate 90 Around X
set t(1,1) 0 ; set t(2,2) 0
set t(1,2) -1 ; set t(2,1) 1
3d_m44 3d_mat t 3d_mat
3d_ident t ;# T3 -- rotate to eye line
set t(0,0) [set t(2,2) [expr -$3d(ey) / $xy]]
set t(0,2) [expr $3d(ex) / $xy]
set t(2,0) [expr -$t(0,2)]
3d_m44 3d_mat t 3d_mat
3d_ident t ;# T4 -- Rotate To Eye Line
set t(1,1) [set t(2,2) [expr $xy / $xyz]]
set t(1,2) [expr $3d(ez) / $xyz]
set t(2,1) [expr -$t(1,2)]
3d_m44 3d_mat t 3d_mat
3d_ident t ;# T5 -- Left-Handed Coords
set t(2,2) -1
3d_m44 3d_mat t 3d_mat
3d_ident t ;# N - Scale By D/S
set t(0,0) [set t(1,1) 4]
3d_m44 3d_mat t 3d_mat
}
##+##########################################################################
#
# 3d_ident matrix
#
# Returns $mm as the identity matrix of size 4
#
proc 3d_ident mm {
upvar 1 $mm m
catch "uplevel [list unset $mm]" ;# Erase all entries
foreach a {0,1 0,2 0,3 1,0 1,2 1,3 2,0 2,1 2,3 3,0 3,1 3,2} {
set m($a) 0
}
set m(0,0) [set m(1,1) [set m(2,2) [set m(3,3) 1.0]]]
}
##+##########################################################################
#
# 3d_m44 ma mb mc
#
# Matrix multiply ma x mb => mc of size 4. mc can be either ma or mb.
#
proc 3d_m44 {ma mb mc} {
upvar 1 $ma aa
upvar 1 $mb bb
upvar 1 $mc cc
for {set r 0} {$r < 4} {incr r} {
set result($r,0) [expr .0 + $aa($r,0)*$bb(0,0) + $aa($r,1)*$bb(1,0) \
+ $aa($r,2)*$bb(2,0) + $aa($r,3)*$bb(3,0)]
set result($r,1) [expr .0 + $aa($r,0)*$bb(0,1) + $aa($r,1)*$bb(1,1) \
+ $aa($r,2)*$bb(2,1) + $aa($r,3)*$bb(3,1)]
set result($r,2) [expr .0 + $aa($r,0)*$bb(0,2) + $aa($r,1)*$bb(1,2) \
+ $aa($r,2)*$bb(2,2) + $aa($r,3)*$bb(3,2)]
set result($r,3) [expr .0 + $aa($r,0)*$bb(0,3) + $aa($r,1)*$bb(1,3) \
+ $aa($r,2)*$bb(2,3) + $aa($r,3)*$bb(3,3)]
}
catch "uplevel [list unset $mc]"
foreach arr [array names result] {
set cc($arr) $result($arr)
}
}
##+##########################################################################
#
# 3d_obj2screen
#
# Converts a 3d position into 2d screen coordinates based on the current
# transformation matrix 3d_mat set up by 3d_init.
#
proc 3d_obj2screen {x y z} {
global 3d_mat 3d
set xe [expr $x*$3d_mat(0,0)+$y*$3d_mat(1,0)+$z*$3d_mat(2,0)+$3d_mat(3,0)]
set ye [expr $x*$3d_mat(0,1)+$y*$3d_mat(1,1)+$z*$3d_mat(2,1)+$3d_mat(3,1)]
set ze [expr $x*$3d_mat(0,1)+$y*$3d_mat(1,2)+$z*$3d_mat(2,2)+$3d_mat(3,2)]
set sx [expr $3d(cx) + ($xe / $ze) * $3d(sx)]
set sy [expr $3d(cx) - ($ye / $ze) * $3d(sy)]
return [list $sx $sy]
}
##+##########################################################################
#
# 3d_axis
#
# Draws x,y,z axes
#
proc 3d_axis {c} {
$c delete axis
set o [3d_obj2screen 0 0 0]
$c create line $o [3d_obj2screen 1.2 0 0] -fill black -arrow last -tag axis
$c create line $o [3d_obj2screen 0 1.2 0] -fill black -arrow last -tag axis
$c create line $o [3d_obj2screen 0 0 1.2] -fill black -arrow last -tag axis
}
##+##########################################################################
#
# Xyz
#
# Creates the subwindow with XYZ scales.
#
proc xyz {w title tag values} {
global eyex eyey eyez centerx centery centerz num_steps
catch {set x [expr round([lindex $values 0])]}
catch {set y [expr round([lindex $values 1])]}
catch {set z [expr round([lindex $values 2])]}
set values [list $x $y $z]
frame $w
pack $w -side left -expand y;# -pady .1i
label $w.ltitle -text $title -relief raised -bd 3
bind $w.ltitle <Double-Button-1> reeye
pack $w.ltitle -side top -fill x
foreach l {x y z} { ;# Create 3 scales for x,y,z
frame $w.f$l -bd 2 -relief raised ;# Holds scale & label
scale $w.f$l.$l -from 10 -to 0 -relief ridge -length 75
$w.f$l.$l config -var 3d(e$l) ;# -comm "redraw"
bind $w.f$l.$l <ButtonRelease-1> "after 1 redraw"
label $w.f$l.l$l -text [string toupper $l]
$w.f$l.l$l config -bg [lindex [$w.f$l.$l config -bg] 4]
pack $w.f$l -side left -expand yes
pack $w.f$l.l$l $w.f$l.$l -side top -fill x
$w.f$l.$l set [lindex $values 0] ;# Set the scale value
set values [lrange $values 1 end]
}
}
##+##########################################################################
#
# redraw
#
# Updates 3d stuff when eye position changes
#
proc redraw {} {
global param
3d_init
triag
}
##+##########################################################################
#
# reeye
#
# Repositions the eye to the default location
#
proc reeye {} {
global 3d
set 3d(ex) 5 ; set 3d(ey) 4 ; set 3d(ez) 3
redraw
}
##+##########################################################################
#############################################################################
#############################################################################
set go 0 ;# Animation off
set param 0 ;# Time parameter
set colors {red green blue cyan slateblue magenta chocolate yellow}
3d_init ;# Initialize the 3d world
do_display ;# Draw the display
triag ;# Draw initial shape