I'm making a GUI visualization for a genetic algorithm, and want to be able to plot the maximization function and the points of each generation of individuals over this function. For each generation, I want only the points to be erased, and the next ones plotted over the already existing function plot.
Using the function plot/dc, I'm achieving to animate each generation of individuals, plotting all the points from a list of coordinates. But this method redraw the canvas each time it runs. So I can't plot the function itself, behind the points. I could make a list of function and points for each generation, but this would be a waste of resources.
Using this code you should be able to simulate my actual state of development.
#lang racket
(require racket/gui plot)
(define main-window (new frame% [label "FUNCTION AND POINTS"] [width 200] [height 600]))
(define canvas-panel (new panel% [parent main-window]))
(define function-canvas (new canvas% [parent canvas-panel]))
(define (plot-points list-of-points)
(for-each
(λ (population)
(plot/dc (points population
#:x-min 0
#:x-max 3
#:y-min 0
#:y-max 9
#:color 'red)
(send function-canvas get-dc)
0 0
(- (send canvas-panel get-width) 40)
(- (send canvas-panel get-height) 40))
(sleep/yield 1))
list-of-points))
(send main-window show #t)
(plot-points '(((1 8) (2 5) (2.5 2))
((2 5) (1.5 6.5) (2 3))
((1.5 3) (2 2) (1.5 3.5))
((2 7) (0.5 1) (2 0.5))
((0.5 9) (0 5) (0.5 0))
((0 1) (1 4.5) (0 8.5))))
Note: The points above was randomly generated, and doesn't correspond to the genetic algorithm output, so there isn't a function that can match this coordinates.
I expect to plot the function graphic behind these points, to be able to see the maximization occurring.
There's a lot being asked here. But essentially you want to put your plot on a non all-white background, that way you can put something else (in this case a function plot) in the background. You can do this with the background-alpha parameter.
Adding this line to your above code:
(plot-background-alpha 0)
will have the points continually build on top of each other. Since you indicated youw anted the points to move, you will also need to clear the screen between each 'frame', you can do this by changing your draw function to:
(define (plot-points list-of-points)
(for-each
(λ (population)
(define dc (send function-canvas get-dc))
(send dc clear)
(plot/dc .... elided ....
dc
0 0
(- (send canvas-panel get-width) 40)
(- (send canvas-panel get-height) 40))
(sleep/yield 1))
list-of-points))
Now to draw your actual background. You could recalculate it each frame, but as you indicated that would be too slow.1 So we can calculate it once, render it as an image, and redraw that each 'frame'. Say the function you want in the background is (+ (sin (* 5 x)) 3), your code would look like:
(define plot-func (function (λ (x) (+ (sin (* 5 x)) 3))
0 (* 2 pi)))
(define plot-background
(plot-bitmap plot-func
#:x-min 0
#:x-max 3
#:y-min 0
#:y-max 9
#:width (- (send canvas-panel get-width) 40)
#:height (- (send canvas-panel get-height) 40)))
Note that get-width and get-height won't store the actual canvas's width/height until after the show method is called.
And now we need to update the draw function to draw this plot to the background:
(define (plot-points list-of-points)
(for-each
(λ (population)
(define dc (send function-canvas get-dc))
(send dc clear)
(send dc draw-bitmap plot-background 0 0)
... elided ...
(sleep/yield 1))
list-of-points))
Putting it all together gives:
#lang racket
(require racket/gui plot)
(define main-window (new frame% [label "FUNCTION AND POINTS"] [width 200] [height 600]))
(define canvas-panel (new panel% [parent main-window]))
(define function-canvas (new canvas% [parent canvas-panel]))
(send main-window show #t)
(plot-background-alpha 0)
(define plot-func (function (λ (x) (+ (sin (* 5 x)) 3))
0 (* 2 pi)))
(define plot-background
(plot-bitmap plot-func
#:x-min 0
#:x-max 3
#:y-min 0
#:y-max 9
#:width (- (send canvas-panel get-width) 40)
#:height (- (send canvas-panel get-height) 40)))
(define (plot-points list-of-points)
(for-each
(λ (population)
(define dc (send function-canvas get-dc))
(send dc clear)
(send dc draw-bitmap plot-background 0 0)
(plot/dc (points population
#:x-min 0
#:x-max 3
#:y-min 0
#:y-max 9
#:color 'red)
dc
0 0
(- (send canvas-panel get-width) 40)
(- (send canvas-panel get-height) 40))
(sleep/yield 1))
list-of-points))
(plot-points '(((1 8) (2 5) (2.5 2))
((2 5) (1.5 6.5) (2 3))
((1.5 3) (2 2) (1.5 3.5))
((2 7) (0.5 1) (2 0.5))
((0.5 9) (0 5) (0.5 0))
((0 1) (1 4.5) (0 8.5))))
1Obviously this depends on the details of what you are calculating and how fast you want to draw it. It might actually be fast enough. ;)
Related
I have the following function to scale a (2-col) matrix:
(define (scale-matrix matrix scale)
(map (lambda (row)
(list (* scale (car row))
(* scale (cadr row))))
matrix))
(scale-matrix '((1 2) (3 4)) 3)
; ((3 6) (9 12))
However, I'm having a hard time converting it into an inline curried call. Here is where I am at so far:
(map
(lambda (row)
(lambda (scale)
(list (* scale (car row))
(* scale (cadr row)))))
'((1 2) (3 4)))
; (#<procedure:...esktop/sicp/021.scm:54:3> #<procedure:...esktop/sicp/021.scm:54:3>)
What would be the proper way to pass both the scale and matrix here? In other words, where to put the 3 ?
The closest I've gotten thus far is to sort of hardocde the 3 in there:
(map
(lambda (row)
((lambda (scale)
; 3 hardcoded, nil placeholder. How to actually 'call' with 3?
(list (* 3 (car row)) (* 3 (cadr row)))) nil))
'((1 2) (3 4)))
Or, is it required that I pass the scale as the first argument? It seems to work that way, though not sure if that's required (or even why that works!)
((lambda (scale)
(map (lambda (row)
(list (* scale (car row)) (* scale (cadr row))))
'((1 2) (3 4)))) 3)
; ((3 6) (9 12))
Your last attempt is correct: you'll have to extract the lambda used for scale outside the map call. You can't modify the innermost lambda, map expects a lambda with one argument, you can't pass a nested lambda there. So if you want to curry the scale there's no option but:
((lambda (scale)
(map (lambda (row)
(list (* scale (car row))
(* scale (cadr row))))
'((1 2) (3 4))))
3)
=> '((3 6) (9 12))
As to why it works, it's like any other anonymous lambda call. Let's see a simpler example, this:
(define (add1 n)
(+ 1 n))
(add1 41)
When evaluated is equivalent to this:
((lambda (n)
(+ 1 n))
41)
Incidentally, the above is also how a let is expanded and evaluated:
(let ((n 41))
(+ 1 n))
So you could also inline the code as shown below; but why do you want to curry it, anyway? the original code with the procedure is just right.
(let ((scale 3))
(map (lambda (row)
(list (* scale (car row))
(* scale (cadr row))))
'((1 2) (3 4))))
I am trying following code:
#lang racket/gui
(require 2htdp/image)
; list of images to be animated:
(define images (list (circle 30 "outline" "red")
(circle 20 "outline" "red")
(circle 10 "outline" "red")
(circle 5 "outline" "red")))
(define (image->bitmap image) ;from: https://lists.racket-lang.org/users/archive/2014-December/065110.html
(let* ([width (image-width image)]
[height (image-height image)]
[bm (make-bitmap width height)]
[dc (send bm make-dc)])
(send dc clear)
(send image draw dc 0 0 0 0 width height 0 0 #f)
bm))
(define frame (new frame% [label "Frame"] [width 300] [height 300]))
(define canvas (new canvas% [parent frame]))
(define a-dc (send canvas get-dc))
(send frame show #t)
(sleep/yield 1)
(define x 20)
(define y 20)
(define (thunk)
(let loop () ; endless loop
(for ((i images))
(send a-dc draw-bitmap (image->bitmap i) x y)
(sleep 0.5))
(loop)))
(thread thunk)
(set! x 100)
(set! y 100)
(thread thunk)
(set! x 200)
(set! y 200)
(thread thunk)
However, only third animation runs while others show only the largest circle (first image). Apparently, previous threads also take new x and y values. How can I have them preserve initially sent x and y values? I cannot have any argument in thunk function because it has to be sent to thread function that needs an argument-less function argument! (I hope my complex statement/argument is functionally correct).
Where is the problem?
Each thread is referring to the same x and y coordinates. This means all threads are drawing at the same place.
Here is an example that shows that having two different sets of x and y variables work.
#lang racket/gui
(require 2htdp/image)
; list of images to be animated:
(define images (list (circle 30 "outline" "red")
(circle 20 "outline" "red")
(circle 10 "outline" "red")
(circle 5 "outline" "red")))
(define (image->bitmap image) ;from: https://lists.racket-lang.org/users/archive/2014-December/065110.html
(let* ([width (image-width image)]
[height (image-height image)]
[bm (make-bitmap width height)]
[dc (send bm make-dc)])
(send dc clear)
(send image draw dc 0 0 0 0 width height 0 0 #f)
bm))
(define frame (new frame% [label "Frame"] [width 300] [height 300]))
(define canvas (new canvas% [parent frame]))
(define a-dc (send canvas get-dc))
(send frame show #t)
(sleep/yield 1)
(define x 20)
(define y 20)
(define (thunk)
(let loop () ; endless loop
(for ((i images))
(send a-dc draw-bitmap (image->bitmap i) x y)
(sleep 0.5))
(loop)))
(thread thunk)
(define X 100)
(define Y 100)
(define (thunk2)
(let loop () ; endless loop
(for ((i images))
(send a-dc draw-bitmap (image->bitmap i) X Y)
(sleep 0.5))
(loop)))
(thread thunk2)
Rather than having variables for each coordinate, consider storing them in a data structure (for example a hash table) and letting the threads read the coordinates from the data structure.
It would be simpler to have a single drawing function that draws all objects at once.
However if you want multiple threads, then use a function to create the thunk:
(define (make-thunk n)
(lambda ()
(let loop () ... )))
use n to pick out the coordinates of the n'th object ...)) Then create the threads with (thread (make-thunk 0)) (thread (make-thunk 1))
Based on answer by #soegaard I found following animateImageList fn to be very convenient for showing a list of images at multiple places and with different speeds:
#lang racket/gui
(require 2htdp/image)
(define (image->bitmap image) ;from: https://lists.racket-lang.org/users/archive/2014-December/065110.html
(let* ([width (image-width image)]
[height (image-height image)]
[bm (make-bitmap width height)]
[dc (send bm make-dc)])
(send dc clear)
(send image draw dc 0 0 0 0 width height 0 0 #f)
bm))
(define (animateImageList imglist sent-dc x y secs)
(λ ()
(let loop ()
(for ((i imglist))
;(send sent-dc clear) ; this may be added to clear previous image;
(send sent-dc draw-bitmap (image->bitmap i) x y)
(sleep secs))
(loop))))
(define imglist (list (circle 40 "outline" "yellow")
(circle 30 "outline" "red")
(circle 20 "outline" "blue")
(circle 10 "outline" "green")
(circle 5 "outline" "black")))
(define frame (new frame% [label "Frame"] [width 300] [height 300]))
(define canvas (new canvas% [parent frame]))
(define a-dc (send canvas get-dc))
(send frame show #t)
(sleep/yield 1)
(thread (animateImageList imglist a-dc 10 10 0.1))
(thread (animateImageList imglist a-dc 100 100 0.4))
(thread (animateImageList imglist a-dc 200 200 0.7))
Output:
The on-tick clause includes an option to change the clock tick rate. I have included my code to dynamically change the value depending on the world state value. The code is not working and I can't understand why. Another issue - how are world programs debugged? The "step" option doesn't work.
; physical constants
(define HEIGHT 300)
(define WIDTH 100)
(define YDELTA 3)
; graphical constants
(define BACKG (empty-scene WIDTH HEIGHT))
(define ROCKET (rectangle 5 30 "solid" "red"))
(define ROCKET-CENTER (/ (image-height ROCKET) 2))
(define ROCKET-XPOS 10)
; LRCD -> LRCD
(define (main1 s)
(big-bang s
[to-draw show]
[on-key launch]
[on-tick fly (clock-rate s)]))
; LRCD -> Image
; renders the state as a resting or flying rocket
(define (show x)
(cond
[(string? x) (rocket-ht HEIGHT)]
[(<= -3 x -1)
(place-image (text (number->string x) 20 "red")
ROCKET-XPOS (* 3/4 WIDTH)
(rocket-ht HEIGHT))]
[(>= x 0)
(rocket-ht x)]))
; LRCD -> image
; positions the rocket at correct height
(define (rocket-ht ht)
(place-image ROCKET ROCKET-XPOS (- ht ROCKET-CENTER) BACKG))
; LRCD KeyEvent -> LRCD
; starts the count-down when space bar is pressed,
; if the rocket is still resting
(define (launch x ke)
(cond
[(string? x) (if (string=? ke " ") -3 x)]
[else x]))
; LRCD -> LRCD
; raises the rocket by YDELTA,
; if it is moving already
(define (fly x)
(cond
[(string? x) x]
[(<= -3 x -1) (if (= x -1) HEIGHT (add1 x))]
[else (- x YDELTA)]))
(define (clock-rate s)
(cond
[(number? s) (if (< s 0) 1 1/28)]
[else 1/28]))
I found this page explaining that some of the gimp functions won't return values consistently, so I implemented a do while loop to make sure the functions are returning pairs before using car. Still, I get the error Error: ( : 1) car: argument 1 must be: pair, but I'm not sure how that's possible as it should keep running the function until it returns a pair.
(define (script-fu-scratchpad drawable)
(let* ((imgHeight 0)
(imgWidth)
(bpp)
(pixel))
(set! imgHeight (gimp-drawable-height drawable))
(do ()
[(pair? imgHeight)]
(set! imgHeight (gimp-drawable-height drawable)))
(set! imgHeight (car imgHeight))
(set! imgWidth (gimp-drawable-width drawable))
(do ()
[(pair? imgWidth)]
(set! imgWidth (gimp-drawable-width drawable)))
(set! imgWidth (car imgWidth))
(set! bpp (gimp-drawable-bpp drawable))
(do ()
[(pair? bpp)]
(set! bpp (gimp-drawable-bpp drawable)))
(set! bpp (car bpp))
(display bpp) (newline)
(set! pixel (cons-array bpp 'byte))
(aset pixel 0 150)
(aset pixel 1 150)
(aset pixel 2 150)
(aset pixel 3 0)
(gimp-drawable-set-pixel drawable (/ imgHeight 2) (/ imgWidth 2) bpp pixel)
(gimp-context-set-background '(100 100 100))
(define county 0)
(define countx 0)
(do ()
[(= countx imgWidth)]
(do ()
[(= county imgHeight)]
(gimp-drawable-set-pixel drawable county countx bpp pixel)
(set! county (+ county 1)))
(set! countx (+ countx 1)))))
In response to GoZoner, I edited it and received the following error: Error: (:1) car: argument 1 must be: pair
(define
(script-fu-scratchpad drawable)
(let*
(
(imgHeight 0)
(imgWidth 0)
(bpp 0)
(pixel 0)
)
(set! imgHeight (gimp-drawable-height drawable))
(set! imgWidth (gimp-drawable-width drawable))
(set! bpp (gimp-drawable-bpp drawable))
(do ()
[(pair? bpp)]
(set! bpp (gimp-drawable-bpp drawable))
)
(set! bpp (car bpp))
(display bpp) (newline)
(set! pixel (cons-array bpp 'byte))
(aset pixel 0 150)
(aset pixel 1 150)
(aset pixel 2 150)
(aset pixel 3 0)
(gimp-drawable-set-pixel drawable (/ imgHeight 2) (/ imgWidth 2) bpp pixel)
(gimp-context-set-background '(100 100 100))
(define county 0)
(define countx 0)
(do ()
[(= countx imgWidth)]
(do ()
[(= county imgHeight)]
(gimp-drawable-set-pixel drawable county countx bpp pixel)
(set! county (+ county 1))
)
(set! countx (+ countx 1))
)
)
)
A couple of things.
In your highest level let* you should be initializing each of the
variables rather than just imgHeight or none of them. Actual
Scheme requires all to be initialized.
Based on name along, I wouldn't expect (gimp-drawable-height drawable) to return a list/cons; it should return a height as a number. Therefore:
I can't imagine (pair? imgHeight) would ever be true
I would expect (car imgHeight) to fail - and it apparently has based on the error you've reported.
The function aset is presumably acting on a multidimensional ((>= rank 2)) array. Therefore its 'index' argument ought to have more then just a single integer. But, perhaps aset is just simply vector-ref in GIMP's scripting variant.
[EDIT: to be more specific] I've annotated your code
(set! bpp (gimp-drawable-bpp drawable)) ; bpp is NOT a pair
(do ()
[(pair? bpp)] ; bpp is NOT a pair
(set! bpp (gimp-drawable-bpp drawable)))
(set! bpp (car bpp)) ; bpp is NOT a pair => ERROR
So I'm going through the first chapter of How To Design Programs 2nd Edition. I believe I made pretty good progress. But there's a "suggestion" to add another graphic to the grid. Every time I try I get an error. At this point, I'm stuck. Below is the code and the error.
Note: the ROCKET image is in the Chapter 1. I just copy and pasted it into the IDE.
Note: The "suggestion" is: How would change the program so that the rocket lands on a flat rock bed that is 10 pixels higher than the bottom of the scene? Don’t forget to change the scenery, too.
HTDP Chapter 1
Here's code that works.
(define BOARDWIDTH 200)
(define BOARDHEIGHT 200)
(define STARTPOSITION 50)
(define BOARDBKGR "blue")
(define GAMEBOARD (empty-scene BOARDWIDTH BOARDHEIGHT BOARDBKGR))
(define ROCKET .)
(define UFO (overlay (circle 10 "solid" "red")
(rectangle 40 4 "solid" "green")))
(define FLATBED (rectangle 60 10 "outline" "black"))
(define (SPACESHIP option)
(cond
[(= option 1) ROCKET]
[(= option 2) UFO]))
(define SHOWNSHIP (SPACESHIP 1))
(define V 20) ;Velocity
(define A 1) ;Acceleration
(define (distance t) ;t = Time
(- (* V t) (* 1/2 A (sqr t))))
(define SPACESHIP-BOTTOM (- BOARDHEIGHT (/ (image-height SHOWNSHIP) 2)))
(define (render-shownship x y)
(place-image SHOWNSHIP x y GAMEBOARD))
(define (create-rocket-scene.v7 t)
(cond
[(<= (distance t) SPACESHIP-BOTTOM)
(render-shownship STARTPOSITION (distance t))]
[(> (distance t) SPACESHIP-BOTTOM)
(render-shownship STARTPOSITION SPACESHIP-BOTTOM)]))
Here's the code that doesn't work:
(define BOARDWIDTH 200)
(define BOARDHEIGHT 200)
(define STARTPOSITION 50)
(define BOARDBKGR "blue")
(define GAMEBOARD (empty-scene BOARDWIDTH BOARDHEIGHT BOARDBKGR))
(define ROCKET .)
(define UFO (overlay (circle 10 "solid" "red")
(rectangle 40 4 "solid" "green")))
(define FLATBED (rectangle 60 10 "outline" "black"))
(define (SPACESHIP option)
(cond
[(= option 1) ROCKET]
[(= option 2) UFO]))
(define SHOWNSHIP (SPACESHIP 1))
(define V 20) ;Velocity
(define A 1) ;Acceleration
(define (distance t) ;t = Time
(- (* V t) (* 1/2 A (sqr t))))
(define SPACESHIP-BOTTOM (- BOARDHEIGHT (/ (image-height SHOWNSHIP) 2)))
(define (render-shownship x y)
(place-image SHOWNSHIP x y GAMEBOARD)
(place-image FLATBED STARTPOSITION 195 GAMEBOARD)) ;offender
(define (create-rocket-scene.v7 t)
(cond
[(<= (distance t) SPACESHIP-BOTTOM)
(render-shownship STARTPOSITION (distance t))]
[(> (distance t) SPACESHIP-BOTTOM)
(render-shownship STARTPOSITION SPACESHIP-BOTTOM)]))
And the error I get is:
define: expected only one expression for the function body, but found
1 extra part
place-image always takes 4 arguments - the image to be placed, x and y coordinates, and the scene (background) on which to place the image. The problem in your code is that the expression (place-image FLATBED STARTPOSITION 195) is providing only 3 inputs to place-image.
So, back up a little and consider: what does the first expression produce? (place-image SHOWNSHIP x y GAMEBOARD) produces a game board scene with a ship on it, correct? Now on top of that scene you further want to place the FLATBED. So instead of sequencing the place-image function calls, instead consider composing them - i.e. what do you think the missing piece is in (place-image FLATBED STARTPOSITION 195 ____)? upon what scene do you want to place the FLATBED? (Hint: we just answered that above). What expression produces that scene? (hint: you already have that expression).
If you understand the idea, you see that to place multiple images on a scene, you compose or nest the function calls (instead of sequencing them as you are attempting):
(place-image img1 x1 y1 (place-image img2 x2 y2 ...))