## formula calculators

A small calculator can be useful to evaluate a single formula involving a few parameters/variables. This formula calculator class constructs a small calculator which evaluates the formula or its inverse with respect to a particular parameter, according to specified code for each result. RVB

First, in incr tcl:

``` class ForCalc {
public variable title "formula calculator" {
if {[info exists _Comp(text)]} {
configure \$_Comp(text) \$title
}
}
private variable _Info
private variable _Comp
# ========================================================================
# constants that can be used in recalculation code
# ========================================================================
common _pi_   [expr {acos(-1)}]
common _tabs_ 273.16          ;# T(O deg C) deg K
common _kb_   1.380622e-23    ;# (J/K)  : boltzmann constant
common _kbev_ 8.61708e-05     ;# (eV/K) : boltzmann constant
common _hbar_ 6.58218e-16     ;# (eV-s) : planck constant
common _co_   2.99792e+10     ;# (cm/s) : speed of light in vacuum
common _qe_   1.602192e-19    ;# (C)    : unit charge
common _eo_   8.854215e-14    ;# (F/cm) : permittivity of free space
common _eox_  3.45314385e-13  ;# F/cm   : permittivity of SiO2
common _esi_  1.03594315e-12  ;# F/cm   : permittivity of Si
common _mo_   9.10956e-31     ;# (kg)   : electron rest mass
# ========================================================================
# METHOD: constructor
# ARGUMENTS:
#   % par_specs
#     list of lists of:
#       parameter name
#       parameter label for calculator display
#       unit label for calculator display
#       default value of the parameter
#       recalculation key: pointer to code to evaluate
#         when return is typed in parameter entry box
#   % recalc_specs
#     list of lists of:
#       recalculation key
#       recalculation code
#     recalculation code should set the value(s) of one or more of the
#       parameters specified in par_specs
#   % args
#       CONFIGURATION-OPTIONS
# CONFIGURATION-OPTIONS:
#   % -title <title>
#       specify the calculator title
# ========================================================================
constructor {par_specs recalc_specs args} {
foreach {par label unit value recalc} \$par_specs {
lappend _Info(pars) \$par
set _Info(\$par-label)  \$label
set _Info(\$par-unit)   \$unit
set _Info(\$par-value)  \$value
set _Info(\$par-recalc) \$recalc
}
foreach {key code} \$recalc_specs {
set _Info(\$key-code)   \$code
}
eval configure \$args
_gui
}
# ========================================================================
# METHOD: destructor
# RESULTS:
#   * destroys calculator gui
# ========================================================================
destructor {
if {[info exists _Comp(top)]} {
destroy \$_Comp(top)
}
}
# ========================================================================
# METHOD: _gui (private)
# PURPOSE: build calculator gui
# ========================================================================
private method _gui {} {
set top .[namespace tail \$this]
toplevel \$top
set _Comp(top) \$top
#---------------------------------------------------------------------
# main layout
#---------------------------------------------------------------------
frame   \$top.text -borderwidth 2 -relief raised
pack    \$top.text -side top -expand 1 -fill both
frame   \$top.pars -borderwidth 2 -relief raised
pack    \$top.pars -side top -expand 1 -fill both
set _Comp(text) \$top.text.text
set _Comp(quit) \$top.text.quit
message \$_Comp(text) -justify center -text \$title -aspect 800
pack    \$_Comp(text) -side left -expand 1 -fill both
button  \$_Comp(quit) -text quit -command "delete object \$this"
pack    \$_Comp(quit) -side left -padx 10 -pady 10 -expand 1 -fill x
#---------------------------------------------------------------------
# parameter entry
#---------------------------------------------------------------------
foreach par \$_Info(pars) {
set _Comp(\$par-frame) \$top.pars.\$par
set _Comp(\$par-label) \$_Comp(\$par-frame).l
set _Comp(\$par-entry) \$_Comp(\$par-frame).e
set _Comp(\$par-unit)  \$_Comp(\$par-frame).u
frame \$_Comp(\$par-frame)
pack  \$_Comp(\$par-frame) -side top   -expand 1 -fill x
label \$_Comp(\$par-label) -text \$_Info(\$par-label) -width 30 -anchor w
pack  \$_Comp(\$par-label) -side left  -expand 1 -fill x
entry \$_Comp(\$par-entry) -relief sunken
pack  \$_Comp(\$par-entry) -side left  -expand 1 -fill x
label \$_Comp(\$par-unit)  -text \$_Info(\$par-unit)  -width 15 -anchor c
pack  \$_Comp(\$par-unit)  -side left  -expand 1 -fill x
\$_Comp(\$par-entry) insert 0 \$_Info(\$par-value)
bind  \$_Comp(\$par-entry) <Return> [code \$this _recalculate \$par]
}
_recalculate [lindex \$_Info(pars) 0]
wm title \$top \$title
}
# ========================================================================
# METHOD: _recalculate (private)
# PURPOSE: recalculate parameter values
# ARGUMENTS:
#   % par_changed
#       name of parameter which had <Return> in entry
# ========================================================================
private method _recalculate {par_changed} {
foreach par \$_Info(pars) {
set \$par [\$_Comp(\$par-entry) get]
}
set recalc \$_Info(\$par_changed-recalc)
if {[info exists _Info(\$recalc-code)]} {
eval \$_Info(\$recalc-code)
}
foreach par \$_Info(pars) {
\$_Comp(\$par-entry) delete 0 end
\$_Comp(\$par-entry) insert 0 [set \$par]
}
}
}

Here is a basic tcl version

namespace eval ::forcalc {
namespace export ForCalc
variable Info
variable Comp
# ========================================================================
# constants that can be used in recalculation code
# ========================================================================
variable _pi_   [expr {acos(-1)}]
variable _tabs_ 273.16          ;# T(O deg C) deg K
variable _kb_   1.380622e-23    ;# (J/K)  : boltzmann constant
variable _kbev_ 8.61708e-05     ;# (eV/K) : boltzmann constant
variable _hbar_ 6.58218e-16     ;# (eV-s) : planck constant
variable _co_   2.99792e+10     ;# (cm/s) : speed of light in vacuum
variable _qe_   1.602192e-19    ;# (C)    : unit charge
variable _eo_   8.854215e-14    ;# (F/cm) : permittivity of free space
variable _eox_  3.45314385e-13  ;# F/cm   : permittivity of SiO2
variable _esi_  1.03594315e-12  ;# F/cm   : permittivity of Si
variable _mo_   9.10956e-31     ;# (kg)   : electron rest mass
# ========================================================================
# PROC: ForCalc
# PURPOSE: create and configure a new calculator
# ARGUMENTS:
#   % this
#     a unique calculator handle
#   % par_specs
#     list of lists of:
#       parameter name
#       parameter label for calculator display
#       unit label for calculator display
#       default value of the parameter
#       recalculation key: pointer to code to evaluate
#         when return is typed in parameter entry box
#   % recalc_specs
#     list of lists of:
#       recalculation key
#       recalculation code
#     recalculation code should set the value(s) of one or more of the
#       parameters specified in par_specs
#   % args
#       CONFIGURATION-OPTIONS
# CONFIGURATION-OPTIONS:
#   % -title <title>
#       specify the calculator title
# ========================================================================
proc ForCalc {this par_specs recalc_specs args} {
variable Info
variable Comp
if {[info exists Comp(\$this-top)]} {
}
set title "formula calculator"
while {[llength \$args] > 0} {
set arg  [lindex \$args 0]
set args [lrange \$args 1 end]
switch -- \$arg {
-title {
set title [lindex \$args 0]
set args  [lrange \$args 1 end]
}
default {
error "option \"\$arg\" is not supported"
}
}
}
foreach {par label unit value recalc} \$par_specs {
lappend Info(\$this-pars) \$par
set Info(\$this-\$par-label)  \$label
set Info(\$this-\$par-unit)   \$unit
set Info(\$this-\$par-value)  \$value
set Info(\$this-\$par-recalc) \$recalc
}
foreach {key code} \$recalc_specs {
set Info(\$this-\$key-code)   \$code
}
set top .\$this
toplevel \$top
set Comp(\$this-top) \$top
frame   \$top.text -borderwidth 2 -relief raised
pack    \$top.text -side top -expand 1 -fill both
frame   \$top.pars -borderwidth 2 -relief raised
pack    \$top.pars -side top -expand 1 -fill both
message \$top.text.msg -justify center -text \$title -aspect 800
pack    \$top.text.msg -side left -expand 1 -fill both
button  \$top.text.but -text quit \
-command [list ::forcalc::_destroy \$this]
pack    \$top.text.but -side left -padx 10 -pady 10 -expand 1 -fill x
#---------------------------------------------------------------------
# parameter entry
#---------------------------------------------------------------------
foreach par \$Info(\$this-pars) {
set Comp(\$this-\$par-frame) \$top.pars.\$par
set Comp(\$this-\$par-label) \$Comp(\$this-\$par-frame).l
set Comp(\$this-\$par-entry) \$Comp(\$this-\$par-frame).e
set Comp(\$this-\$par-unit)  \$Comp(\$this-\$par-frame).u
frame \$Comp(\$this-\$par-frame)
pack  \$Comp(\$this-\$par-frame) -side top   -expand 1 -fill x
label \$Comp(\$this-\$par-label) -text \$Info(\$this-\$par-label) \
-width 30 -anchor w
pack  \$Comp(\$this-\$par-label) -side left  -expand 1 -fill x
entry \$Comp(\$this-\$par-entry) -relief sunken
pack  \$Comp(\$this-\$par-entry) -side left  -expand 1 -fill x
label \$Comp(\$this-\$par-unit)  -text \$Info(\$this-\$par-unit)  \
-width 15 -anchor c
pack  \$Comp(\$this-\$par-unit)  -side left  -expand 1 -fill x
\$Comp(\$this-\$par-entry) insert 0 \$Info(\$this-\$par-value)
bind  \$Comp(\$this-\$par-entry) <Return> \
[list forcalc::_recalculate \$this \$par]
}
_recalculate \$this [lindex \$Info(\$this-pars) 0]
wm title \$top \$title
return \$this
}
# ========================================================================
# METHOD: _destroy
# ARGUMENTS:
#   % this
#       The calculator handle
# RESULTS:
#   * destroys calculator gui
#   * unregisters calculator handle
# ========================================================================
proc _destroy {this} {
variable Info
variable Comp
destroy \$Comp(\$this-top)
foreach item [array names Info \$this-*] {
unset Info(\$item)
}
foreach item [array names Comp \$this-*] {
unset Comp(\$item)
}
}
# ========================================================================
# PROC: _recalculate
# PURPOSE: recalculate parameter values
# ARGUMENTS:
#   % this
#       the calculator handle
#   % par_changed
#       name of parameter which had <Return> in entry
# ========================================================================
proc _recalculate {this par_changed} {
variable Info
variable Comp
variable _pi_
variable _tabs_
variable _kb_
variable _kbev_
variable _hbar_
variable _co_
variable _qe_
variable _eo_
variable _eox_
variable _esi_
variable _mo_
foreach par \$Info(\$this-pars) {
set \$par [\$Comp(\$this-\$par-entry) get]
}
set recalc \$Info(\$this-\$par_changed-recalc)
if {[info exists Info(\$this-\$recalc-code)]} {
eval \$Info(\$this-\$recalc-code)
}
foreach par \$Info(\$this-pars) {
\$Comp(\$this-\$par-entry) delete 0 end
\$Comp(\$this-\$par-entry) insert 0 [set \$par]
}
}
}
namespace import forcalc::ForCalc

Here are several examples:

A field mowing calculator.  Calculates the total time required to mow
a field at a constant speed and swath and number of turns, or the number
of turns required given total time, swath, and speed.

ForCalc mowcalc {
n     "number of turns"  {}    43  t
dr    "swath"            ft     3  n
s     "speed"            ft/s   3  n
t     "total time"       min  100  n
} {
t {set t [expr {\$n*(\$n+1)*(\$dr*\$_pi_)/(\$s*60.0)}]}
n {set n [expr {sqrt(0.25 + (\$s*60.0*\$t)/(\$dr*\$_pi_)) - 0.5}]}
} -title "field mow calculator"

An integrated resistor calculator.  Given a target resistance and width,
calculates the required resistor length.  Given a specified length and width,
calculates the resistance.

ForCalc rescalc {
r    "Resistance"       ohms           1000 l
rho  "Sheet Resistance" ohms/square    200  r
dw   "delta W"          um             0.02 r
dl   "delta L"          um            -0.10 r
l    "Length"           um             10.0 r
w    "Width"            um              1.0 l
} {
l {set l [expr {1.0*\$r*(\$w-\$dw)/\$rho + \$dl}]}
r {set r [expr {1.0*\$rho*(\$l-\$dl)/(\$w-\$dw)}]}
} -title "integrated circuit resistance calculator"

A fixed-rate mortgage payment calculator.

ForCalc mortcalc {
principal      "Principal"     \$   100000    payment
years          "Years"         y   30        payment
rate           "Rate"          %   8.00      payment
payments_year  "Payments/Year" {}  12        payment
payment        "Payment"       \$   733.76    principal
} {
payment {
set a            [expr {1.0e-2*\$rate/\$payments_year}]
set N            [expr {1.0*\$payments_year*\$years}]
set per_thousand [expr {1.0e+3*\$a/(1-pow(1+\$a,-\$N))}]
set payment      [expr {1.0e-3*\$principal*\$per_thousand}]
set payment      [format "%-10.2f" \$payment]
}
principal {
set a            [expr {1.0e-2*\$rate/\$payments_year}]
set N            [expr {1.0*\$payments_year*\$years}]
set per_thousand [expr {1.0e+3*\$a/(1-pow(1+\$a,-\$N))}]
set principal    [expr {1.0e+3*\$payment/\$per_thousand}]
set principal    [format "%-10.2f" \$principal]
}
} -title "fixed rate mortgage calculator"

A watering calculator.

ForCalc watercalc {
area  "area"             ft^2   1000 t
gpm   "gallons/minute"   gal/min 1.5 t
d     "depth"            in      0.5 t
t     "total time"       hour    1.5 d
} {
t {
set ft3_g [expr {231.0/1728.0}]
set t [expr {(1.0*\$area*(1.0*\$d/12))/(60.0*\$gpm*\$ft3_g)}]
}
d {
set ft3_g [expr {231.0/1728.0}]
set d [expr {(12.0*(\$t*60.0)*\$gpm*\$ft3_g)/\$area}]
}
} -title "watering calculator"```

 Category Calculator Category GUI Category Mathematics Category Application Category Itcl