Hearter than I thought: LISP McCLIM DRAWING


*Prepare for valentine's day by drawing a heart with LISP McCLIM!

I'm not an artist. Prahou is the show artist.

Setting-stuff-up I'll link in an appendix. Let's just roll into some lisp.

1. Loading mcclim using quicklisp

I guess you got into lisp using M-x slime in emacs or $ sbcl in shell.

(ql:quickload :mcclim)

2. Using mcclim clim

(in-package :clim-user)

This package is nicely set up for being a user of clim.

3. Make an application frame at all

(define-application-frame heart ()
  ((current-ink :initform +red+))
  (:pane :application :display-function 'display-function))

=+red+= has red ink inside of it.

3.1. Drawing a heart.

We can incrementally change clim stuff by

  1. define-application-frame heart'ing again. This doesn't actually "redo everything". It's smart. It only does the changes.
  2. Redefining 'display-function over and over again to try different things. It works better than it sounds.

If you're not playing along properly, remember that lisp, and particularly clim interactively and semi/automatically handle "errors" very, very well. Nothing bad happens, ever.

It's a bit random, but this is just what display-function(s) do:

3.1.1. Display-function

(defun display-function (frame pane)
  (draw-circle* pane 60 80 25))
  1. We're ready to run it
    (find-application-frame 'heart)
    

    congratulations. I guess we should use that blue ink. Here we begin just redefining display-function over and over. Sure, whatever better way you just thought of is better.

  2. A red heart.
    (defun display-function (frame pane)
      (with-slots (current-ink) frame
        (with-drawing-options (pane :ink current-ink)
          (draw-circle* pane 60 80 25))))
    

    and try it again

    (find-application-frame 'heart)
    

    Okay! Moving on up in this world.

  3. A second circle.
    (defun display-function (frame pane)
      (with-slots (current-ink) frame
        (with-drawing-options (pane :ink current-ink)
          (draw-circle* pane 60 80 25)
          (draw-circle* pane 100 80 25))))
    

    and try it again

    (find-application-frame 'heart)
    

    Er, let's move these circles differently for clarity. And let's make them not filled and things.

    (defun display-function (frame pane)
      (with-slots (current-ink) frame
        (with-drawing-options (pane :ink current-ink)
          (with-translation (pane 0 0)
            (draw-circle* pane 60 80 25 :filled nil))
          (with-translation (pane 40 0)
            (draw-circle* pane 60 80 23 :filled nil)))))
    
  4. Only specific arcs
    (defun display-function (frame pane)
      (with-slots (current-ink) frame
        (with-drawing-options (pane :ink current-ink)
          (with-translation (pane 0 0)
            (draw-circle* pane 60 80 25 :filled nil
                          :start-angle 0.0 :end-angle (+ pi (/ pi 6))))
          (with-translation (pane 40 0)
            (draw-circle* pane 60 80 25 :filled nil
                          :start-angle (* pi -1/6)
                                        :end-angle pi)))))
    

    Well, hey, we got into some math stuff after all. Er, with a radius of 25, I guess

    (list (* rad (cos (* pi 1/6)))
          (* rad (sin (* pi 1/6))))
    
    <colgroup> <col class="org-right" /> <col class="org-right" /> </colgroup>
    21.65063509461097d0 12.499999999999998d0

    We can tell intuitively what directions we have to go these magnitudes in.

  5. Fiddling with that trig
    (defun display-function (frame pane &aux (rad 25))
      (with-slots (current-ink) frame
        (with-drawing-options (pane :ink current-ink)
          (with-translation (pane 60 80)
            (draw-circle* pane 0 0 rad :filled nil
                                       :start-angle 0.0 :end-angle (+ pi (/ pi 6)))
            (destructuring-bind
                (x y)
                (list (* rad (cos (* pi 1/6)))
                      (* rad (sin (* pi 1/6))))
              (let* ((dx (+ (* 2 rad) (- (* 2 (- rad x)) )))
                     (dy (* dx (- (/ x y)))))
                (draw-line* pane
                            (- x)
                            y
                            (+ (- x) dx)
                            (+ y (- dy))))
              ))
    
          (with-translation (pane (+ 60 40) 80)
            (draw-circle* pane 0 0 rad :filled nil
                          :start-angle (* pi -1/6)
                                        :end-angle pi)
            (destructuring-bind
                (x y)
                (list (* rad (cos (* pi 1/6)))
                      (* rad (sin (* pi 1/6))))
              (let* ((dx (+ (* 2 rad) (- (* 2 (- rad x)) )))
                     (dy (* dx (- (/ x y)))))
                (draw-line* pane
                            (+ x)
                            y
                            (- x dx)
                            (+ y (- dy))))))
    
                    )))
    
  6. Let's get rid of those straggly bits
    (defun display-function (frame pane &aux (rad 25))
        (with-slots (current-ink) frame
          (with-drawing-options (pane :ink current-ink)
            (with-translation (pane 60 80)
              (draw-circle* pane 0 0 rad :filled nil
                                         :start-angle (/ pi 6) :end-angle (+ pi (/ pi 6)))
              (destructuring-bind
                  (x y)
                  (list (* rad (cos (* pi 1/6)))
                        (* rad (sin (* pi 1/6))))
                (let* ((dx (+ (* 2 rad) (- (* 2 (- rad x)) )))
                       (dy (* dx (- (/ x y)))))
                  (draw-line* pane
                              (- x)
                              y
                              (+ (- x) dx)
                              (+ y (- dy))))
                ))
    
            (with-translation (pane (+ 60 40) 80)
              (draw-circle* pane 0 0 rad :filled nil
                            :start-angle (* pi -1/6)
                                          :end-angle (- pi (/ pi 6)))
              (destructuring-bind
                  (x y)
                  (list (* rad (cos (* pi 1/6)))
                        (* rad (sin (* pi 1/6))))
                (let* ((dx (+ (* 2 rad) (- (* 2 (- rad x)) )))
                       (dy (* dx (- (/ x y)))))
                  (draw-line* pane
                              (+ x)
                              y
                              (- x dx)
                              (+ y (- dy))))))
    
                      )))
    

4. Verdict

I'm giving myself a solid 6 out of 10 for valentines day.

</div>

Author: Screwlisp.

Created: 2025-01-31 Fri 20:06

Validate

Get lispmoo2

Comments

Log in with itch.io to leave a comment.

Errr I said I would include beginner material. Hey, I did a bunch of peertube videos a while ago:

Talk with me on the mastodon:

https://mastodon.sdf.org/@screwtape

(Hang out at the weekly show btw)

and then bug McCLIM's JackDaniel instead:

https://turtleware.eu/

Or @mdhughes : 

https://mdhughes.tech/software/arrokoth/