1 ############################################################
3 # Copyright (C) 1998 William F. Schelter #
4 # For distribution under GNU public License. See COPYING. #
6 # Modified by Jaime E. Villate #
7 # Time-stamp: "2024-03-28 14:51:52 villate" #
8 ############################################################
12 {dxdt
"x-y^2+sin(x)*.3" {specifies dx
/dt
= dxdt. eg
-dxdt "x+y+sin(x)^2"} }
13 {dydt
"x+y" {specifies dy
/dt
= dydt. eg
-dydt "x-y^2+exp(x)"} }
14 {dydx
"" { may specify dy
/dx
= x^
2+y
,instead of dy
/dt
= x^
2+y and dx
/dt
=1 }}
15 {vectors blue
"Color for the vectors"}
16 {fieldlines red
"Color for the fieldlines"}
17 {curves
"" "Color for the orthogonal curves"}
18 {xradius
10 "Width in x direction of the x values" }
19 {yradius
10 "Height in y direction of the y values"}
20 {width
700 "Width of canvas in pixels"}
21 {height
600 "Height of canvas in pixels" }
22 {scrollregion
{} "Area to show if canvas is larger" }
23 {xcenter
0.0 {(xcenter
,ycenter
) is the origin of the window
}}
24 {ycenter
0.0 "see xcenter"}
25 {bbox
"" "xmin ymin xmax ymax .. overrides the -xcenter etc"}
26 {tinitial
0.0 "The initial value of variable t"}
27 {nsteps
300 "Number of steps to do in one pass"}
28 {xfun
"" "A semi colon separated list of functions to plot as well"}
29 {tstep
"" "t step size"}
30 {direction
"both" "May be both, forward or backward" }
31 {versus_t
0 "Plot in a separate window x and y versus t, after each trajectory" }
32 {windowname
".plotdf" "window name"}
33 {windowtitle
"Plotdf" "window title"}
34 {parameters
"" "List of parameters and values eg k=3,l=7+k"}
35 {linecolors
{ green black brown gray black
} "colors for functions plots"}
36 {sliders
"" "List of parameters ranges k=3:5,u"}
37 {trajectory_at
"" "Place to calculate trajectory"}
38 {linewidth
"1.5" "Width of integral lines" }
39 {nolines
0 "If not 0, plot points and nolines"}
40 {plotpoints
0 "if not 0 plot the points at pointsize" }
41 {pointsize
2 "radius in pixels of points" }
42 {autoscale
"x y" "Set {x,y}center and {x,y}range depending on data and function. "}
43 {errorbar
0 "If not 0 width in pixels of errorbar. Two y values supplied for each x: {y1low y1high y2low y2high .. }"}
44 {data
"" "List of data sets to be plotted. Has form { {xversusy {x1 x2 ... xn} {y1 .. yn ... ym}} .. {againstIndex {y1 y2 .. yn}} .. }"}
45 {labelposition
"10 15" "Position for the curve labels nw corner"}
46 {xaxislabel
"" "Label for the x axis"}
47 {yaxislabel
"" "Label for the y axis"}
48 {psfile
"" "A filename where the graph will be saved in PostScript."}
49 {nobox
0 "if not zero, do not draw the box around the plot."}
50 {axes
"xy" "if zero, no axes are drawn. x, y or xy to draw the axes."}
51 {narrows
"15" "Minimum number of arrows to draw in x and y directions"}
52 {nolegend
0 "if not zero, do not write down the legend."}
53 {bargraph
0 "If not 0 this is the width of the bars on a bar graph" }
56 proc makeFrameDf
{ win
} {
57 set w
[makeFrame
$win df
]
58 if { $w eq
"." } { set w
"" }
59 makeLocal
$win c dydx buttonFont type
62 catch { set top
[winfo parent
$win]}
64 wm title
$top {Xmaxima
: plotdf
}
65 wm iconname
$top "plotdf"
68 ttk
::button $mb.versust
-text [mc
"Time Plot"] -command "plotVersusT $win"
69 ttk
::button $mb.replot
-text [mc
"Replot"] -command "replot$type $win"
70 pack $mb.replot
$mb.versust
-side left
75 proc swapChoose
{win msg winchoose
} {
76 # global dydx dxdt dydt
77 if { "$msg" == "dydt" } {
78 pack $winchoose.dxdt
-before $winchoose.dydt
-side bottom
80 $winchoose.dydt.lab config
-text "dy/dt:"
82 pack forget
$winchoose.dxdt
85 $winchoose.dydt.lab config
-text "dy/dx:"}}
87 proc setForIntegrate
{ win
} {
89 # $c delete printrectangle
90 bind $c <1> "doIntegrateScreen $win %x %y "
94 # proc xff { t x y } { return [expr {$x + $y }] }
95 # proc yff { t x y } { return [expr {$x - $y }] }
97 proc doIntegrateScreen
{ win sx sy
} {
99 doIntegrate
$win [storx
$win [$c canvasx
$sx]] [story
$win [$c canvasy
$sy]]
102 proc doIntegrate
{ win x0 y0
} {
103 makeLocal
$win xradius yradius c dxdt dydt tinitial tstep nsteps
\
104 direction linewidth tinitial versus_t xmin xmax ymin ymax parameters
\
106 linkLocal
$win didLast trajectoryStarts
107 set linewidth
[expr {$linewidth*[vectorlength
$width $height]/1000.
}]
108 set arrowshape
[scalarTimesVector
$linewidth {3 5 2}]
110 # method can be rungeKutta, rungeKuttaA or adamsMoulton
111 set method
{adamsMoulton
}
112 oset
$win trajectory_at
[format "%.10g %.10g" $x0 $y0]
113 lappend trajectoryStarts
[list $x0 $y0]
115 # puts "doing at $trajectory_at"
116 # A reasonabel value of tstep has already been set up in drawDF by
117 # using the maximum length of the field vectors. This is just in case.
118 if {$tstep eq
{}} {set tstep
0.1}
121 switch -- $direction {
122 forward
{ set todo
{1}}
123 backward
{ set todo
{-1}}
124 both
{ set todo
{-1 1}}}
126 foreach task
{fieldlines curves
} {
127 set color
[oget
$win $task]
128 if {($color ne
{}) && ($color ne
{blank
})} {
130 lappend useColors
$task $color}}
132 foreach task
$tasks {
134 set linecolor
[assoc
$task $useColors]
137 if {$task eq
{curves
}} {
138 setCurvesFunctions
$dxdt $dydt $parameters
141 setFieldFunctions
$dxdt $dydt $parameters}
144 if {$task eq
{fieldlines
}} {
149 if {$direction eq
{forward
}} {
152 set h
[expr {$sgn*$tstep}]
153 set form
[list $method xff yff
$tinitial $x0 $y0 $h $nsteps]
155 # puts "doing: $form"
156 # pts will be a list with values of t, x and y, at the initial
157 # point and at each of the steps
159 lappend didLast
$form
161 # puts "clipping box: ($x1,$y1), ($x2,$y2)"
162 foreach {t xr yr
} $pts {
164 set p1
[list $xr $yr]
165 set c1
[PointCode
$p1 $xmin $ymin $xmax $ymax]
166 # puts "point $p1 with code $c1"
168 set coords
[list [rtosx
$win $xr] [rtosy
$win $yr]]
169 } else {set coords
{}}
172 set p2
[list $xr $yr]
173 set c2
[PointCode
$p2 $xmin $ymin $xmax $ymax]
174 # puts "point $p2 with code $c2"
177 [ClipLine
$p1 $p2 $c1 $c2 $xmin $ymin $xmax $ymax]
178 if {[llength $clip]} {
180 lappend coords
[rtosx
$win [lindex $p 0]]
181 lappend coords
[rtosy
$win [lindex $p 1]]}}
182 if {$c2 && ([llength $coords] >= 4)} {
183 $c create line
$coords -tags path
-width \
184 $linewidth -fill $linecolor -arrow $arrow \
185 -arrowshape $arrowshape
188 lappend coords
[rtosx
$win [lindex $p2 0]]
189 lappend coords
[rtosy
$win [lindex $p2 1]]}
192 if {[llength $coords] >= 4} {
193 $c create line
$coords -tags path
-width $linewidth \
194 -fill $linecolor -arrow $arrow -arrowshape $arrowshape}}}
195 if { $versus_t } { plotVersusT
$win}
198 proc plotVersusT
{ win
} {
199 linkLocal
$win didLast dydt dxdt parameters xcenter xradius ycenter yradius
200 if { $didLast == {} } { return }
201 set w
[winfo parent
$win]
202 if {$w eq
{.
}} { set w
{}}
203 set xwin .versust.plotx
204 set ywin .versust.ploty
210 desetq
"tinitial x0 y0 h steps" [lrange $v 3 end
]
211 set this
[lrange $v 0 4]
212 if { [info exists doing
($this) ] } { set tem
$doing($this) } else {
216 set allx
""; set ally
""; set allt
""
217 foreach {t x y
} $ans {
218 if {($x>=$xcenter-1.1*$xradius) && ($x<=$xcenter+1.1*$xradius)
219 && ($y>=$ycenter-1.1*$yradius) && ($y<=$ycenter+1.1*$yradius)} {
223 foreach u
$tem v
[list $allx $ally $allt] {
224 if { $h > 0 } { lappend doing
($this) [concat $u $v]} else {
225 lappend doing
($this) [concat [lreverse
$v] $u]}}}
227 foreach {na val
} [array get doing
] {
228 lappend xdata
[list xaxislabel
"t"]
229 lappend xdata
[list yaxislabel
[oget
$win xaxislabel
]]
230 lappend xdata
[list plotpoints
0] [list nolegend
1]
231 lappend xdata
[list xversusy
[lindex $val 2] [lindex $val 0] ]
232 lappend ydata
[list xaxislabel
"t"]
233 lappend ydata
[list yaxislabel
[oget
$win yaxislabel
]]
234 lappend ydata
[list plotpoints
0] [list nolegend
1]
235 lappend ydata
[list xversusy
[lindex $val 2] [lindex $val 1] ]}
236 if { ![winfo exists .versust
] } {toplevel .versust
}
237 # puts "plotdata: $plotdata"
238 plot2d
-data $xdata -windowname $xwin -ycenter $xcenter -yradius $xradius
239 wm title
$xwin [concat [oget
$win xaxislabel
] [mc
" versus t"]]
240 plot2d
-data $ydata -windowname $ywin -ycenter $ycenter -yradius $yradius
241 wm title
$ywin [concat [oget
$win yaxislabel
] [mc
" versus t"]]
244 proc lreverse
{ lis
} {
247 while { [incr i
-1]>=0 } {
248 lappend ans
[lindex $lis $i]
253 proc drawArrowScreen
{ c atx aty dfx dfy color
} {
254 set win
[winfo parent
$c]
255 makeLocal
$win width height
256 set linewidth
[expr {[vectorlength
$width $height]/1000.
}]
257 set arrowshape
[scalarTimesVector
$linewidth {3 5 2}]
258 set x1
[expr {$atx + $dfx}]
259 set y1
[expr {$aty + $dfy}]
260 $c create line
$atx $aty $x1 $y1 -tags arrow
-fill $color -arrow last
\
261 -arrowshape $arrowshape -width $linewidth }
263 proc drawDF
{ win tinitial
} {
265 makeLocal
$win xmin xmax xcenter ycenter c ymin ymax transform vectors
\
266 xaxislabel yaxislabel nobox axes width height narrows tstep
271 set stepsize
[expr {$width/($narrows+2.0)}]
272 set stepy
[expr {$height/($narrows+2.0)}]
273 if { $stepy < $stepsize } {set stepsize
$stepy}
274 set margin
[expr {0.7*$stepsize}]
278 set xfactor
[lindex $transform 0]
279 set yfactor
[lindex $transform 3]
280 set uptox
[expr {[$rtosx $xmax] - $margin}]
281 set uptoy
[expr {[$rtosy $ymin] - $margin}]
283 #puts "draw [$rtosx $xmin] to $uptox"
284 if {($vectors ne
"") && ($vectors ne
"blank") } {
285 for {set x
[expr {[$rtosx $xmin] + $margin}]} {$x < $uptox} \
286 {set x
[expr {$x + $stepsize}]} {
287 for {set y
[expr {[$rtosy $ymax] + $margin}]} {$y < $uptoy} \
288 {set y
[expr {$y + $stepsize}]} {
289 set args
"$t0 [$storx $x] [$story $y]"
290 set dfx
[expr {$xfactor * [eval xff
$args]}]
291 # screen y is negative of other y
292 set dfy
[expr {$yfactor * [eval yff
$args]}]
294 set len
[vectorlength
$dfx $dfy]
295 append all
" $len $dfx $dfy "
296 if { $min > $len } {set min
$len}
297 if { $max < $len } {set max
$len}}}
298 if {$tstep eq
{}} {oset
$win tstep
[expr {$stepsize/(10.0*$max)}]}
299 set arrowmin
[expr {0.25*$stepsize}]
300 set arrowrange
[expr {0.85*$stepsize - $arrowmin}]
301 set s1
[expr {($arrowrange*$min+$arrowmin*$min-$arrowmin*$max)/($min-$max)}]
302 set s2
[expr {$arrowrange/($max-$min)}]
303 # we calculate fac for each length, so that
304 # when we multiply the vector times fac, its length
305 # will fall somewhere in [arrowmin,arrowmin+arrowrange].
306 # Vectors of length min and max resp. should get mapped
307 # to the two end points.
308 # To do this we set fac [expr {$s1/$len + $s2}]
310 for {set x
[expr {[$rtosx $xmin] + $margin}]} {$x < $uptox} \
311 {set x
[expr {$x + $stepsize}]} {
312 for {set y
[expr {[$rtosy $ymax] + $margin}]} {$y < $uptoy} \
313 {set y
[expr {$y + $stepsize}]} {
314 set len
[lindex $all [incr i
]]
315 set dfx
[lindex $all [incr i
]]
316 set dfy
[lindex $all [incr i
]]
317 #puts "[$storx $x] [$story $y] x=$x y=$y dfx=$dfx dfy=$dfy
318 # puts "$len $dfx $dfy"
320 set fac
[expr {$s1/$len + $s2}]
321 drawArrowScreen
$c $x $y [expr {$fac * $dfx}] \
322 [expr {$fac * $dfy} ] $vectors}}}}
323 set x1
[rtosx
$win $xmin]
324 set y1
[rtosy
$win $ymax]
325 set x2
[rtosx
$win $xmax]
326 set y2
[rtosy
$win $ymin]
330 if { $xmin*$xmax < 0 && ($axes == {y
} ||
$axes == {xy
}) } {
332 $c create line
[$rtosx 0] $y1 [$rtosx 0] $y2 -fill $axisGray \
335 $c create line
[$rtosx 0] $y1 [$rtosx 0] $y2 -width 2 \
336 -arrow "first" -tags axes
}}
337 if { $ymin*$ymax < 0 && ($axes == {x
} ||
$axes == {xy
}) } {
339 $c create line
$x1 [$rtosy 0] $x2 [$rtosy 0] -fill $axisGray \
342 $c create line
$x1 [$rtosy 0] $x2 [$rtosy 0] -width 2 \
343 -arrow "last" -tags axes
}}
345 if { "[$c find withtag printrectangle]" == "" && $nobox == 0 } {
346 $c create rectangle
$x1 $y1 $x2 $y2 -tags printrectangle
-width 2
347 marginTicks
$c [storx
$win $x1] [story
$win $y2] [storx
$win $x2] \
348 [story
$win $y1] "printrectangle marginticks"}
349 # Write down the axes labels
351 set width
[oget
$win width
]
352 set height
[oget
$win height
]
353 if {$nobox != 0 && $xmin*$xmax < 0 && ($axes == {y
} ||
$axes == {xy
})} {
354 set xbound
[expr { [$rtosx 0] - 0.08*$width}]
356 set xbound
[expr {$x1-0.08*$width}]
358 $c create
text $xbound [expr {($y1+$y2)/2.0}] -anchor center
-angle 90 \
359 -text [oget
$win yaxislabel
] -font {helvetica
16 normal
} -tags axislabel
360 if {$nobox != 0 && $ymin*$ymax < 0 && ($axes == {x
} ||
$axes == {xy
})} {
361 $c create
text [expr {$x2-0.01*$width}] \
362 [expr { [$rtosy 0]+0.02*$height}] -anchor ne
-tags axislabel
\
363 -text [oget
$win xaxislabel
] -font {helvetica
16 normal
}
365 $c create
text [expr {($x1 + $x2)/2}] [expr {$y2 + 0.08*$height}] \
366 -anchor center
-text [oget
$win xaxislabel
] \
367 -font {helvetica
16 normal
} -tags axislabel
371 proc parseOdeArg
{ s
} {
374 set exp
"\[dD]$w\\($w\(\[xyz])$w,$w\(\[xyt])$w\\)$w=(\[^;]+)"
375 while { [regexp -- $exp $s junk x t
expr ] } {
376 lappend ans
-d${x
}d
$t
378 regexp -indices $exp $s junk x t
expr
379 set s
[string range
$s [lindex $junk 1] end
]
381 if { ![info exists ans
] ||
([llength $ans] == 2 && "[lindex $ans 0]" != "-dydx") } {
382 error [mc
"bad -ode argument:\n$orig\nShould be d(y,x)=f(x,y)
383 OR d(x,t)=f(x,y) d(y,t)=g(x,y)"]
388 proc plotdf
{ args
} {
389 global plotdfOptions printOption printOptions plot2dOptions
391 # to see options add: -debug 1
392 set win
[assoc
-windowname $args]
393 if { "$win" == "" } {set win
[getOptionDefault windowname
$plotdfOptions] }
394 if { "[set ode [assoc "-ode" $args]]" != "" } {
395 set args
[delassoc
-ode $args]
396 set args
[concat [parseOdeArg
$ode] $args]
399 getOptions
$plotdfOptions $args -usearray [oarray
$win]
401 # Makes extra vertical space for sliders
402 linkLocal
$win sliders height
403 if {[string length
$sliders] > 0} {
404 oset
$win height
[expr {$height + 40*[llength [split $sliders ,]]}]}
408 if { "$dydx" !="" } { oset
$win dxdt
1 ; oset
$win dydt
$dydx }
409 setPrintOptions
$args
410 foreach v
{trajectoryStarts recompute
} {
411 catch { unset [oloc
$win $v] }
415 oset
$win sliderCommand sliderCommandDf
416 oset
$win trajectoryStarts
""
418 oset
$win maintitle
[concat "makeLocal $win dxdt dydt dydx ;" \
419 {if { "$dydx" == "" } { concat "dx/dt = $dxdt , dy/dt = $dydt"} else {
420 concat "dy/dx = $dydt" } } ]
424 proc replotdf
{ win
} {
425 global printOption plotdfOptions
426 linkLocal
$win xfundata data psfile
427 if { ![info exists data
] } {set data
""}
428 makeLocal
$win c dxdt dydt tinitial nsteps xfun trajectory_at parameters
429 set_xy_region
$win 0.8
430 set_xy_transforms
$win
431 setFieldFunctions
$dxdt $dydt $parameters
434 oset
$win curveNumber
-1
435 drawDF
$win $tinitial
436 if { $trajectory_at ne
"" } {eval doIntegrate
$win $trajectory_at}
438 foreach v
[sparseListWithParams
$xfun {x y t
} $parameters ] {
439 proc _xf
{ x
} "return \[expr { $v } \]"
440 lappend xfundata
[list nolegend
1] \
441 [linsert [calculatePlot
$win _xf
$nsteps] 0 xversusy
] }
442 redraw2dData
$win -tags path
444 # Create a PostScript file, if requested
445 if { $psfile ne
"" } {
446 set printOption
(psfilename
) $psfile
448 $c delete printoptions
449 eval [$win.menubar.
close cget
-command] }}
451 proc setFieldFunctions
{ dxdt dydt parameters
} {
452 proc xff
{t x y
} "expr {[sparseWithParams $dxdt {x y} $parameters]}"
453 proc yff
{t x y
} "expr {[sparseWithParams $dydt {x y} $parameters] } "
455 proc setCurvesFunctions
{ dxdt dydt parameters
} {
456 proc xff
{t x y
} "expr {[sparseWithParams $dydt {x y} $parameters]}"
457 proc yff
{t x y
} "expr {[sparseWithParams -($dxdt) {x y} $parameters]}"
460 proc doConfigdf
{ win
} {
461 desetq
"wb1 wb2" [doConfig
$win]
462 makeLocal
$win buttonFont
464 set frdydx
$wb1.choose1
465 button $frdydx.dydxbut
-command "swapChoose $win dydx $frdydx " \
466 -text "dy/dx" -font $buttonFont
467 button $frdydx.dydtbut
-command "swapChoose $win dydt $frdydx" \
468 -text "dy/dt,dx/dt" -font $buttonFont
469 mkentry
$frdydx.dxdt
[oloc
$win dxdt
] "dx/dt" $buttonFont
470 mkentry
$frdydx.dydt
[oloc
$win dydt
] "dy/dt" $buttonFont
471 pack $frdydx.dxdt
$frdydx.dydt
-side bottom
-fill x
-expand 1
472 pack $frdydx.dydxbut
$frdydx.dydtbut
-side left
-fill x
-expand 1
474 foreach w
{narrows parameters xfun linewidth xradius yradius xcenter ycenter tinitial versus_t nsteps direction curves vectors fieldlines
} {
475 mkentry
$wb1.
$w [oloc
$win $w] $w $buttonFont
476 pack $wb1.
$w -side bottom
-expand 1 -fill x
478 mkentry
$wb1.trajectory_at
[oloc
$win trajectory_at
] \
479 "Trajectory at" $buttonFont
480 bind $wb1.trajectory_at.e
<KeyPress-Return
> \
481 "eval doIntegrate $win \[oget $win trajectory_at\] "
482 pack $wb1.trajectory_at
$frdydx -side bottom
-expand 1 -fill x
483 if { "[oget $win dydx]" != "" } { swapChoose
$win dydx
$frdydx }
487 proc sliderCommandDf
{ win var val
} {
488 linkLocal
$win recompute
489 updateParameters
$win $var $val
490 set com
"recomputeDF $win"
491 # allow for fast move of slider...
492 #mike FIXME: this is a wrong use of after cancel
497 proc recomputeDF
{ win
} {
498 linkLocal
$win recompute
499 if { [info exists recompute
] } {
503 # puts "set recompute 1"
506 linkLocal
$win trajectoryStarts c tinitial dxdt dydt parameters
510 catch {set trajs
$trajectoryStarts}
512 while { $redo != $recompute } {
513 # puts " setFieldFunctions $dxdt $dydt $parameters"
514 setFieldFunctions
$dxdt $dydt $parameters
515 # $c delete path point arrow
517 catch { unset trajectoryStarts
}
521 catch { doIntegrate
$win $x0 $y0 }
523 if { $redo != $recompute } { break }
525 if { $redo == $recompute } {
526 catch { drawDF
$win $tinitial }
529 # puts " unset recompute"
534 ## endsource plotdf.tcl