0
votes

The code below draws me the sine wave throughout the screen, and beyond. However, I want that x never exceeds (width). At that point, the window should be cleared, and the wave should be drawn again, starting at 0. I tried

(set-state! :x (mod (seq->stream x) (width)))

but it didn't work. I checked some examples, tried some code which made use of checking whether (> x (width)), tried to put the logic into the draw function, but they don't work either.

I would like to draw a continuous waveform (like an electrocardiogram) on a limited display. I would appreciate any help.

(ns soso.core
  (:require [quil.core :refer :all]
            [quil.helpers.seqs :refer [seq->stream range-incl]]))

(defn setup []
  (smooth)
  (frame-rate 100)
  (background 255)
  (let [x (range-incl)]
    (set-state! :x (seq->stream x))))

(defn draw []
  (stroke 0)
  (stroke-weight 2)
  (let [x ((state :x))
        y (round (* 100 (abs (sin x))))]
    (ellipse x y 4 4)))

Edit

When I tried to put the logic into the draw function, I wrote (set-state! :x 0) in the setup function, and put the following code into the draw function:

(let [x (if (> ((state :x)) 10)
            0
          (inc ((state :x))))])

But it didn't work, and didn't throw any error in any of the nrepl buffers. To see what happened, I tried this code in the user repl:

(let [x 0]
  (take 20
        (if (> x 10)
            0
          (inc x))))

Even if I tried this code by wrapping the if statement inside (into '()), the code complained with: "IllegalArgumentException Don't know how to create ISeq from: java.lang.Long clojure.lang.RT.seqFrom". It is the same with:

(loop [x 0]
  (if (> x 10)
    0
    (recur (inc x))))

What is wrong with this if statement? The clojure api says that if tests the test condition, and then evaluates and yields then or else branches, accordingly. If it yields, it should spit out that integer, which in turn gets added to the list, which in turn should be listed by take function, I think.

1
Third edit code: I expect this to yield a single 0. The loop will go around with 0, 1, 2... etc until (> x 10) at which point you return 0. - pete23
Second edit code: take applies to a seq. i.e. (take 3 '(1 2 3 4 5 6)). (take 3 2) will give your error - you can't "take the first three items from the value 2" as it's not a sequence. - pete23
First edit code: the "state" offered by quil is essentially a map. In your first example you set :x to 0 in setup, then you try to evaluate it as a function in ((state :x)) - I'm not sure why the error doesn't bubble up to the top, but you should get the same effect running (0) - i.e. ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn. - pete23
Final thought - I think you might benefit from running over a basic clojure tutorial. You need to get the differences between functions / seqs / values clear otherwise you'll run into a muddle. (range-incl) returns a seq, seq->stream returns a function that when repeatedly evaluated will return the values from the seq. The reason for the double bracketing around ((state :x)) is that (state :x) returns a function which you then call using the (). - pete23
You're absolutely right. Thunks and streams were the last parts I learned about DrRacket. When I get tired, I start to mix the meaning of those double brackets. Thank you very much for the clarifications. - barerd

1 Answers

0
votes

Something more like this seems to give the effect you are looking for.

(defn draw []
  (stroke 0)
  (stroke-weight 2)
  (let [x ((state :x))
        y (+ 100 (* 100 (sin (/ x 100))))]
    (ellipse (mod x (width)) y 4 4)))

Couple of issues you are encountering:

  1. The input to sin is a floating value in radians. My hackery of divide by 100 here just makes sure that we're getting a nice continuous output. Practically if you want to cycle every n pixels you need to use 2 PI / n to adjust x.
  2. Rounding off the value is going to give you points, as the domain of sin is 1 to -1. Here I'm adjusting by adding on 100.
  3. Where you fiddle with x will depend on the exact requirement. Here I'm simply wrapping your x value by taking it's modulus in the draw function.

Hope this helps.