Add support for external html docs
[maxima.git] / interfaces / xmaxima / Tkmaxima / Plotdf.tcl
blob38aa7d6717abe992d1eb30c3b86c178ee7927cd0
1 ############################################################
2 # Plotdf.tcl #
3 # Copyright (C) 1998 William F. Schelter #
4 # For distribution under GNU public License. See COPYING. #
5 # #
6 # Modified by Jaime E. Villate #
7 # Time-stamp: "2024-03-28 14:51:52 villate" #
8 ############################################################
10 global plotdfOptions
11 set plotdfOptions {
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
61 set top $win
62 catch { set top [winfo parent $win]}
63 catch {
64 wm title $top {Xmaxima: plotdf}
65 wm iconname $top "plotdf"
67 set mb $w.menubar
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
71 setForIntegrate $w
72 return $win
75 proc swapChoose {win msg winchoose } {
76 # global dydx dxdt dydt
77 if { "$msg" == "dydt" } {
78 pack $winchoose.dxdt -before $winchoose.dydt -side bottom
79 oset $win dydx ""
80 $winchoose.dydt.lab config -text "dy/dt:"
81 } else {
82 pack forget $winchoose.dxdt
83 oset $win dxdt 1
84 oset $win dydx " "
85 $winchoose.dydt.lab config -text "dy/dx:"}}
87 proc setForIntegrate { win} {
88 makeLocal $win c
89 # $c delete printrectangle
90 bind $c <1> "doIntegrateScreen $win %x %y "
93 # sample procedures
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 } {
98 makeLocal $win c
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 \
105 width height
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]
114 set didLast {}
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}
120 set todo {1}
121 switch -- $direction {
122 forward { set todo {1}}
123 backward { set todo {-1}}
124 both { set todo {-1 1}}}
125 set tasks {}
126 foreach task {fieldlines curves} {
127 set color [oget $win $task]
128 if {($color ne {}) && ($color ne {blank})} {
129 lappend tasks $task
130 lappend useColors $task $color}}
131 set taskNo -1
132 foreach task $tasks {
133 incr taskNo
134 set linecolor [assoc $task $useColors]
135 set signs $todo
136 set coords {}
137 if {$task eq {curves}} {
138 setCurvesFunctions $dxdt $dydt $parameters
139 set signs {-1 1}
140 } else {
141 setFieldFunctions $dxdt $dydt $parameters}
142 foreach sgn $signs {
143 set arrow {none}
144 if {$task eq {fieldlines}} {
145 if { $sgn < 0 } {
146 set arrow {first}
147 set coords {}
148 } else {
149 if {$direction eq {forward}} {
150 set arrow {last}
151 set coords {}}}}
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
158 set pts [eval $form]
159 lappend didLast $form
160 set first 1
161 # puts "clipping box: ($x1,$y1), ($x2,$y2)"
162 foreach {t xr yr} $pts {
163 if {$first} {
164 set p1 [list $xr $yr]
165 set c1 [PointCode $p1 $xmin $ymin $xmax $ymax]
166 # puts "point $p1 with code $c1"
167 if {!$c1} {
168 set coords [list [rtosx$win $xr] [rtosy$win $yr]]
169 } else {set coords {}}
170 set first 0
171 } else {
172 set p2 [list $xr $yr]
173 set c2 [PointCode $p2 $xmin $ymin $xmax $ymax]
174 # puts "point $p2 with code $c2"
175 if {$c1|$c2} {
176 set clip \
177 [ClipLine $p1 $p2 $c1 $c2 $xmin $ymin $xmax $ymax]
178 if {[llength $clip]} {
179 foreach p $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
186 set coords {}}
187 } else {
188 lappend coords [rtosx$win [lindex $p2 0]]
189 lappend coords [rtosy$win [lindex $p2 1]]}
190 set p1 $p2
191 set c1 $c2}}
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
205 set xdata {}
206 set ydata {}
208 foreach v $didLast {
209 set ans [eval $v]
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 {
213 set tem ""
215 set doing($this) ""
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)} {
220 lappend allx $x
221 lappend ally $y
222 lappend allt $t}}
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 } {
245 set ans ""
246 set i [llength $lis]
247 while { [incr i -1]>=0 } {
248 lappend ans [lindex $lis $i]
250 return $ans
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 } {
264 global axisGray
265 makeLocal $win xmin xmax xcenter ycenter c ymin ymax transform vectors \
266 xaxislabel yaxislabel nobox axes width height narrows tstep
267 set rtosx rtosx$win
268 set rtosy rtosy$win
269 set storx storx$win
270 set story story$win
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}]
275 set min 1.0E42
276 set max 0.0
277 set t0 $tinitial
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}]
282 # draw the axes:
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]}]
293 # puts "$dfx $dfy"
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}]
309 set i -1
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"
319 if {$len != 0.0} {
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]
328 # Draw the two axes
329 $c del axes
330 if { $xmin*$xmax < 0 && ($axes == {y} || $axes == {xy}) } {
331 if { $nobox == 0 } {
332 $c create line [$rtosx 0] $y1 [$rtosx 0] $y2 -fill $axisGray \
333 -tags axes
334 } else {
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}) } {
338 if { $nobox == 0 } {
339 $c create line $x1 [$rtosy 0] $x2 [$rtosy 0] -fill $axisGray \
340 -tags axes
341 } else {
342 $c create line $x1 [$rtosy 0] $x2 [$rtosy 0] -width 2 \
343 -arrow "last" -tags axes}}
344 # Draw the plot box
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
350 $c del axislabel
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}]
355 } else {
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}
364 } else {
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 } {
372 set orig $s
373 set w "\[ ]*"
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
377 lappend ans $expr
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)"]
385 return $ans
388 proc plotdf { args } {
389 global plotdfOptions printOption printOptions plot2dOptions
390 # puts "args=$args"
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]
398 global [oarray $win]
399 getOptions $plotdfOptions $args -usearray [oarray $win]
400 oset $win didLast {}
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 ,]]}]}
406 makeLocal $win dydx
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] }
414 makeFrameDf $win
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" } } ]
421 replotdf $win
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
432 $c delete all
433 setForIntegrate $win
434 oset $win curveNumber -1
435 drawDF $win $tinitial
436 if { $trajectory_at ne "" } {eval doIntegrate $win $trajectory_at}
437 set xfundata ""
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
447 writePostscript $win
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
463 frame $wb1.choose1
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 }
484 setForIntegrate $win
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
493 after cancel $com
494 after 50 $com
497 proc recomputeDF { win } {
498 linkLocal $win recompute
499 if { [info exists recompute] } {
500 incr recompute
501 return
502 } else {
503 # puts "set recompute 1"
504 set recompute 1
506 linkLocal $win trajectoryStarts c tinitial dxdt dydt parameters
507 set redo 0
508 set trajs ""
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
516 $c delete all
517 catch { unset trajectoryStarts }
518 set redo $recompute
519 foreach pt $trajs {
520 desetq "x0 y0" $pt
521 catch { doIntegrate $win $x0 $y0 }
522 update
523 if { $redo != $recompute } { break }
525 if { $redo == $recompute } {
526 catch { drawDF $win $tinitial }
529 # puts " unset recompute"
530 unset recompute
534 ## endsource plotdf.tcl