![]() |
![]() |
Tutorial |
![]() |
![]() |
![]() |
||||
![]() |
![]() |
![]() |
![]() |
![]() |
This tutorial describes obj, a minimalistic object system for Tcl. As I made it for my project on computer-generated cartoons, the examples in this tutorial deal of a simple system of morphing lines. We create a class line and a class morph which interpolates two lines. The code lines in this tutorial show the dialog of interactive shell tclsh. The percent sign Loading packageEither you have just copied the file into some directory, then input: % source obj-0.1.tm % Or, if you have put it to your package path, then input: % package require obj 0.1 % Class definitionFirst, we create our class % ::obj::class line class line % OptionsEvery line has coords for the start and end point stored in option % ::obj::configure line -coords {10 10 100 10} class line has option -coords with default {10 10 100 10} % First instanceNow is the time for all brave ... err ... well, we try our first object creation: % set line1 [::obj::new line -coords {10 10 10 100}] ::obj::inst::3 % No smoke at the keyboard. Phew. Now let us check if coordinates have value as expected: % $line1 cget -coords 10 10 10 100 % Defining methodsOur line shall be able to % ::obj::method line appear canvas { $canvas delete $self $canvas create line [$self cget -coords] -tags $self } method line appear % ::obj::method line disappear canvas { $canvas delete $self } method line disappear % Now try it out: % package require Tk 8.5.1 % pack [canvas .c] -expand yes -fill both % $line1 appear .c 1 % Screenshot: As expected: a vertical line appears. Trust is cool. Control is hot: Validating optionsTo avoid inappropriate values for option % ::obj::validatemethod line -coords val { foreach c $val { if {![string is double -strict $c]} then { return -code error\ [list $self expects for -coords only numbers\ but received $c]! } } } validatemethod line -coords % If we happen to try to configure our % $line1 configure -coords {x 2 3 4} ::obj::inst::3 expects for -coords only numbers but received x! % Defining a morphing methodThis method destructively moves object's coords towards another line object: % ::obj::method line morph {other f} { set coords {} foreach {x0 y0} [$self cget -coords] {x1 y1} [$other cget -coords] { lappend coords [expr {$x0+($x1-$x0)*$f}] [expr {$y0+($y1-$y0)*$f}] } $self configure -coords $coords } method line morph % Next class: morphNow, the basics are done. We start our morphing class ::obj::class morph class morph % An instance of morph needs a starting line, an ending line, a resulting line, coords for each of them, and a state: % ::obj::configure morph -coords {10 10 10 100} class morph has option -coords with default {10 10 10 100} % ::obj::configure morph -startcoords {10 10 10 100} class morph has option -startcoords with default {10 10 10 100} % ::obj::configure morph -endcoords {100 10 100 100} class morph has option -endcoords with default {100 10 100 100} % ::obj::configure morph -state 0.0 class morph has option -state with default 0.0 % ComponentsObjects can have private data. If one private date is the name of another object, then we can treat this as a component. The constructor of % ::obj::constructor morph {x0 y0 x1 y1 x2 y2 x3 y3} { $self private start [new line -coords "$x0 $y0 $x1 $y1"] $self private end [new line -coords "$x2 $y2 $x3 $y3"] $self private line [new line] } constructor morph % Now, let us test it: % set m [new morph 10 10 10 100 50 50 30 100] ::obj::inst::5 % We can always see all names of private data of the instance: % $m private start end line % See the value of private curve: % $m private line ::obj::inst::8 % We can invoke any defined method of private line: % $m component line cget -coords 10 10 100 10 % DelegationThe methods % ::obj::delegate method appear morph line class morph delegates method appear to component line. % ::obj::delegate method disappear morph line class morph delegates method disappear to component line. % The option % ::obj::delegate option -coords morph start class morph delegates option -coords to component start. % Delegation with different target namesThe option % ::obj::delegate option -startcoords morph start -coords class morph delegates option -startcoords to component start. % ::obj::delegate option -endcoords morph end -coords class morph delegates option -endcoords to component end. % Read-only optionsOption % ::obj::read-only morph -coords option -coords of class morph is read-only. % Actions triggered by configuremethodOur morphing object shall change its component % ::obj::configuremethod morph -state s { $self component line configure -coords\ [$self component start cget -coords] $self component line morph [$self private end] $s } configuremethod morph -state % Now, test it: % $m configure -state 0.5 % $m appear .c 10 % The morphed component line appears on canvas To watch the state of $m continuously, we create a scale which interactively sets the state: % scale .c.s -orient horizontal .c.s % place .c.s -anchor sw -relwidth 1.0 -rely 1.0 % .c.s configure -command [list apply {{obj num} { $obj configure -state [expr {$num / 100.0}] $obj appear .c }} $m] % Screenshot: What remains? Just fine-tuning as you wish. Have fun with object-oriented programming! |