# hoco.tcl -- # # Part of: Useless Widgets Package # Contents: homogeneous coordinates procedures # Date: Wed Nov 24, 2004 # # Abstract # # # # Copyright (c) 2004 Marco Maggi # # The author hereby grant permission to use, copy, modify, distribute, # and license this software and its documentation for any purpose, # provided that existing copyright notices are retained in all copies # and that this notice is included verbatim in any distributions. No # written agreement, license, or royalty fee is required for any of the # authorized uses. Modifications to this software may be copyrighted by # their authors and need not follow the licensing terms described here, # provided that the new terms are clearly indicated on the first page of # each file where they apply. # # IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY PARTY # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY # DERIVATIVES THEREOF, EVEN IF THE AUTHOR HAVE BEEN ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND # NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, # AND THE AUTHOR AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE # MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # # #page ## ------------------------------------------------------------ ## Math procedures. ## ------------------------------------------------------------ proc uwp_math_deg2rad { angle } {expr {double($angle)*0.0174532925199}} proc uwp_math_rad2deg { angle } {expr {double($angle)*57.2957795131}} proc uwp_math_sin_cos { angle nickname } { uplevel [list set cos_$nickname [expr {cos(double($angle))}]] uplevel [list set sin_$nickname [expr {sin(double($angle))}]] } proc uwp_math_evallist { expr_list } { foreach expr $expr_list { lappend matrix [uplevel [list expr $expr]] } return $matrix } interp alias {} uwp_math_fundamental_rotation_around_z {} \ uwp_math_fundamental_rotation t \ {{$cos_t} {-$sin_t} 0 0 {$sin_t} {$cos_t} 0 0 0 0 1 0 0 0 0 1} interp alias {} uwp_math_fundamental_rotation_around_x {} \ uwp_math_fundamental_rotation p \ {1 0 0 0 0 {$cos_p} {-$sin_p} 0 0 {$sin_p} {$cos_p} 0 0 0 0 1} interp alias {} uwp_math_fundamental_rotation_around_y {} \ uwp_math_fundamental_rotation f \ {{$cos_f} 0 {$sin_f} 0 0 1 0 0 {-$sin_f} 0 {$cos_f} 0 0 0 0 1} proc uwp_math_fundamental_rotation { alias expression angle } { uwp_math_sin_cos $angle $alias uwp_math_evallist $expression } proc uwp_math_fundamental_translation {x y z} {list 1 0 0 $x 0 1 0 $y 0 0 1 $z 0 0 0 1} # The "transform_list" argument is a list of the form: # # { # { premultiplication_boolean { a11 a12 a13 ... a44 } # { premultiplication_boolean { b11 b12 b13 ... b44 } # { premultiplication_boolean { c11 c12 c13 ... c44 } # ... # } # # matrices are applied in the same order in which the appear in the # list. proc uwp_math_transformation { transform_list coords } { set matrix_elms {a11 a12 a13 a14 a21 a22 a23 a24 a31 a32 a33 a34 a41 a42 a43 a44} foreach transform $transform_list { foreach {premultiplication matrix} $transform { foreach $matrix_elms $matrix {} set result {} foreach {x y z other} $coords { if { $premultiplication } { lappend result \ [expr {double($x)*double($a11)+double($y)*double($a12)+ double($z)*double($a13)+double($other)*double($a14)}] \ [expr {double($x)*double($a21)+double($y)*double($a22)+ double($z)*double($a23)+double($other)*double($a24)}] \ [expr {double($x)*double($a31)+double($y)*double($a32)+ double($z)*double($a33)+double($other)*double($a34)}] \ [expr {double($x)*double($a41)+double($y)*double($a42)+ double($z)*double($a43)+double($other)*double($a44)}] } else { lappend result \ [expr {double($x)*double($a11)+double($y)*double($a21)+ double($z)*double($a31)+double($other)*double($a41)}] \ [expr {double($x)*double($a12)+double($y)*double($a22)+ double($z)*double($a32)+double($other)*double($a42)}] \ [expr {double($x)*double($a13)+double($y)*double($a23)+ double($z)*double($a33)+double($other)*double($a43)}] \ [expr {double($x)*double($a14)+double($y)*double($a24)+ double($z)*double($a34)+double($other)*double($a44)}] } } set coords $result } } set result {} foreach { x y z o } $coords { if { $o != 1.0 } { set x [expr {$x/$o}] set y [expr {$y/$o}] } lappend result $x $y } return $result } #page ## ------------------------------------------------------------ ## Token management. ## ------------------------------------------------------------ set uwp_counter 0 array set uwp_token_map {} proc uwp_token_get { ns key id } { uwp_token_access $ns $id; return $data($key) } proc uwp_token_declare { ns id } { upvar \#0 uwp_token_map map uwp_counter counter set map($ns:$id) uwp__[incr counter] } proc uwp_token_forget { ns id } { upvar \#0 uwp_token_map map unset -nocomplain $map($ns:$id) unset -nocomplain map($ns:$id) } proc uwp_token_access { ns id } { upvar \#0 uwp_token_map map uplevel [list upvar \#0 $map($ns:$id) data] } proc uwp_token_aliases { prefix namespace } { foreach c {declare forget access get} { interp alias {} $prefix$c {} uwp_token_$c $namespace } } #page ## ------------------------------------------------------------ ## Transform variables and declarations. ## ------------------------------------------------------------ uwp_token_aliases uwp_hoco_transform_token_ hoco_tran # Transform type attributes: # -names = the list of parameter names # -deg2rad = a list of boolean declaring if the parameter requires # conversion from degrees to radians # -defaults = the list of default values for the parameters # -transform = the name of the procedure that converts the parameters # into the transformation proc uwp_hoco_transform_declare { id args } { uwp_hoco_transform_token_declare $id uwp_hoco_transform_token_access $id array set data $args } interp alias {} uwp_hoco_transform_forget {} uwp_hoco_transform_token_forget proc uwp_hoco_transform_compute { id parameters } { uwp_hoco_transform_token_access $id foreach name $data(-names) deg2rad $data(-deg2rad) value $parameters \ { lappend values [expr {($deg2rad)? [uwp_math_deg2rad $value] : $value}] } $data(-transform) $data(-names) $values } proc uwp_hoco_transform_get_parameter_name_index { id parm_name } { uwp_hoco_transform_token_access $id lsearch $data(-names) $parm_name } interp alias {} uwp_hoco_transform_get_parameter_defaults \ {} uwp_hoco_transform_token_get -defaults interp alias {} uwp_hoco_transform_get_parameter_names \ {} uwp_hoco_transform_token_get -names #page ## ------------------------------------------------------------ ## Default transformation matrix procedures. ## ------------------------------------------------------------ proc uwp_hoco_canvas_parameters_to_transform { names values } { foreach $names $values {} uwp_math_sin_cos $theta t list [list yes [uwp_math_evallist \ {{$cos_t} {-$sin_t} 0 {$x} {-$sin_t} {-$cos_t} 0 {$y} 0 0 1 0 0 0 0 1}]] } proc uwp_hoco_homogeneous_parameters_to_transform { names values } { foreach $names $values {} foreach { axis angleName mode } { z theta yes y phi yes x psi no } { lappend result [list $mode \ [uwp_math_fundamental_rotation_around_$axis [set $angleName]]] } lappend result [list yes [uwp_math_fundamental_translation $x $y $z]] } proc uwp_hoco_workspace_parameters_to_transform { names values } { foreach $names $values {} foreach { axis angleName mode } { y phi yes x psi no } { lappend result [list $mode \ [uwp_math_fundamental_rotation_around_$axis [set $angleName]]] } return $result } proc uwp_hoco_dh_parameters_to_transform { names values } { foreach $names $values {} uwp_math_sin_cos $theta t uwp_math_sin_cos $alpha a list [list yes [uwp_math_evallist { {$cos_t} {-($cos_a*$sin_t)} {$sin_a*$sin_t} {double($a)*$cos_t} {$sin_t} {$cos_a*$cos_t} {-($sin_a*$cos_t)} {double($a)*$sin_t} 0 {$sin_a} {$cos_a} {double($d)} 0 0 0 1}]] } proc uwp_hoco_perspective_parameters_to_transform { names values } { foreach $names $values {} list [list yes [uwp_math_evallist { 1 0 0 0 0 1 0 0 0 0 1 0 0 0 {-1.0/double($d)} 1 }]] } #page ## ------------------------------------------------------------ ## Default transform types. ## ------------------------------------------------------------ uwp_hoco_transform_declare canvas \ -names { theta x y } -deg2rad { yes no no } -defaults { 0.0 300.0 300.0 } \ -transform uwp_hoco_canvas_parameters_to_transform uwp_hoco_transform_declare homogeneous \ -names { theta psi phi x y z } -deg2rad { yes yes yes no no no } \ -defaults { 0.0 0.0 0.0 0.0 0.0 0.0 } \ -transform uwp_hoco_homogeneous_parameters_to_transform uwp_hoco_transform_declare workspace \ -names { psi phi } -deg2rad { yes yes } -defaults { 0.0 0.0 } \ -transform uwp_hoco_workspace_parameters_to_transform uwp_hoco_transform_declare dh \ -names { d theta a alpha } -deg2rad { no yes no yes } \ -defaults { 0.0 0.0 0.0 0.0 } \ -transform uwp_hoco_dh_parameters_to_transform uwp_hoco_transform_declare perspective \ -names { d } -deg2rad { no } \ -defaults { 1700 } \ -transform uwp_hoco_perspective_parameters_to_transform #page ## ------------------------------------------------------------ ## Transform instances procedures. ## ------------------------------------------------------------ uwp_token_aliases uwp_hoco_instance_token_ hoco_inst # Transform instance attributes: # -type = the name of a transform type # -parameters = a list holding the current values of the parameters # -transform = the transformation # -dynamic = the list of parameters that are modifiable in this # transform instance, it must be a sub-set of the # transform type parameters or the empty string proc uwp_hoco_instance_declare { id args } { uwp_hoco_instance_token_declare $id uwp_hoco_instance_token_access $id array set data $args if { ! [info exists data(-parameters)] } { set data(-parameters) \ [uwp_hoco_transform_get_parameter_defaults $data(-type)] } uwp_hoco_instance_update_transform $id } interp alias {} uwp_hoco_instance_forget {} uwp_hoco_instance_token_forget proc uwp_hoco_instance_update_transform { id } { uwp_hoco_instance_token_access $id set data(transform) \ [uwp_hoco_transform_compute $data(-type) $data(-parameters)] } proc uwp_hoco_instance_update_parameter { id parm_name parm_value } { uwp_hoco_instance_token_access $id set idx [uwp_hoco_transform_get_parameter_name_index $data(-type) $parm_name] lset data(-parameters) $idx $parm_value uwp_hoco_instance_update_transform $id } proc uwp_hoco_instance_get_transform { transform_names } { foreach id $transform_names \ { uwp_hoco_instance_token_access $id; lappend result $data(transform) } return [join $result] } proc uwp_hoco_instance_get_all_parameter_names { id } { uwp_hoco_instance_token_access $id uwp_hoco_transform_get_parameter_names $data(-type) } proc uwp_hoco_instance_get_dynamic_parameter_names { id } { uwp_hoco_instance_token_access $id return $data(-dynamic) } proc uwp_hoco_instance_get_parameter_value { id parm_name } { uwp_hoco_instance_token_access $id set idx [uwp_hoco_transform_get_parameter_name_index $data(-type) $parm_name] lindex $data(-parameters) $idx } #page ## ------------------------------------------------------------ ## Default transform instances. ## ------------------------------------------------------------ uwp_hoco_instance_declare canvas -type canvas -dynamic {} uwp_hoco_instance_declare world -type homogeneous -dynamic { theta phi psi } #page ## ------------------------------------------------------------ ## Basic graphical elements. ## ------------------------------------------------------------ # Prototype of drawing procedure: # # proc widget_canvas_draw { command coords {main_tag {}} {tags {}} } # # command = what to draw (line, polygon, ...) # coords = the list of homogeneous coordinates of the points # main_tag = the main tag of the object: the one used to delete it # tags = optional list of tags proc uwp_wireframe_draw_reference_frame { frame_tag transforms element } { set axis_template {{-1 0 0 1 1 0 0 1} {0 -1 0 1 0 1 0 1} {0 0 -1 1 0 0 1 1}} foreach coords $axis_template axis_tag { xaxis yaxis zaxis } { widget_canvas_draw line \ [uwp_math_transformation \ [concat [list $element] \ [uwp_hoco_instance_get_transform $transforms]] \ $coords] ${frame_tag}_$axis_tag \ [list $frame_tag reference_frame_$axis_tag reference_frame] } } proc uwp_wireframe_draw_prism { prism_tag transforms element {tags {}} } { foreach {face coords} { A {-1 -1 -1 1 1 -1 -1 1 1 1 -1 1 -1 1 -1 1 -1 -1 -1 1} B {-1 -1 1 1 1 -1 1 1 1 1 1 1 -1 1 1 1 -1 -1 1 1} C {-1 -1 -1 1 -1 -1 1 1 -1 1 1 1 -1 1 -1 1 -1 -1 -1 1} D {1 -1 -1 1 1 -1 1 1 1 1 1 1 1 1 -1 1 1 -1 -1 1} } { widget_canvas_draw line [uwp_math_transformation \ [concat [list $element] [uwp_hoco_instance_get_transform $transforms]] \ $coords] ${prism_tag}_$face $tags } } #page proc uwp_wireframe_draw_workspace { item_tag transforms } { uwp_wireframe_draw_plane ${item_tag}_XY $transforms { 0 4 5 1 5 4 } \ [list workspace_XY_plane $item_tag] uwp_wireframe_draw_plane ${item_tag}_YZ $transforms { 1 5 6 2 6 5 } \ [list workspace_YZ_plane $item_tag] uwp_wireframe_draw_plane ${item_tag}_ZX $transforms { 2 6 0 4 0 6 } \ [list workspace_ZX_plane $item_tag] } proc uwp_wireframe_draw_plane { main_tag transforms indices {tags {}} } { set delta 30.0 set num 11 set max [expr {$delta*double($num-1)}] set coords { 0 0 0 1 0 0 0 1} set counter 0 foreach { a b c } $indices { set vector $coords set plane_tag ${main_tag}_[incr counter] for {set x 0.0} {$x <= $max} {set x [expr {$x+$delta}]} { lset vector $a $x lset vector $b $x lset vector $c $max widget_canvas_draw line \ [uwp_math_transformation \ [uwp_hoco_instance_get_transform $transforms] $vector] \ ${plane_tag}_$x [concat [list $main_tag] $tags] } } } #page proc uwp_wireframe_regular_polygon { number_of_points } { set angle [expr {[uwp_math_deg2rad 360.0]/double($number_of_points)}] lappend coords 1 0 0 1 for {set i 1} {$i < $number_of_points} {incr i} { lappend coords \ [expr {cos(double($angle)*double($i))}] \ [expr {sin(double($angle)*double($i))}] 0 1 } lappend coords 1 0 0 1 } proc uwp_wireframe_draw_path { coords transforms element tag {tags {}} } { widget_canvas_draw line \ [uwp_math_transformation \ [concat [list $element] \ [uwp_hoco_instance_get_transform $transforms]] \ $coords] $tag $tags } ### end of file # Local Variables: # mode: tcl # End:
See also: Playing with planes in 3D which uses this code.