• painting clouds with clojure

    Over the 4th of July weekend I made this little program to generate images of clouds

     

    (ns clouds.core
      (:gen-class)
      (:import [java.awt.image BufferedImage]
               [java.io File]
               [javax.imageio ImageIO]
               [javax.swing JPanel JFrame SwingUtilities]
               [java.awt Graphics Color Dimension RenderingHints]))
    
    (def width 500)
    (def height 500)
    (def num-particles 1000000)
    (def color-cache (atom {}))
    (def output-image? true)
    
    (defn- rand-between [min max]
      (+ (rand-int (- max min)) min))
    
    (defn- make-gray-color [color-val alpha]
      (let [color-key (keyword (str color-val "-" alpha))
            cached-color (color-key @color-cache)]
        (if cached-color
          cached-color
          (let [^Color new-color (Color. color-val
                                         color-val
                                         color-val
                                         alpha)]
            (swap! color-cache assoc color-key new-color)
            new-color))))
    
    (defn- paint-clouds [^Graphics graphics]
      (loop [n 0
             last-x (rand-int width)
             last-y (rand-int height)]
        (let [rand-op (if (< (rand) 0.5) inc dec)
              rand-axis (if (< (rand) 0.5) :vert :horiz)
              new-x (if (= rand-axis :horiz)
                      (rand-op last-x)
                      last-x)
              new-y (if (= rand-axis :vert)
                      (rand-op last-y)
                      last-y)
              rand-gray (rand-between 250 255)
              rand-alpha (rand-int 75)
              neighbor-alpha-modifier 0.11
              particle-color (make-gray-color rand-gray rand-alpha)
              neighbor-color (make-gray-color rand-gray
                                              (int (* rand-alpha
                                                      neighbor-alpha-modifier)))]
          (doall
           (for [x-offset (range -1 2)
                 y-offset (range -1 2)
                 :let [x (+ new-x x-offset)
                       y (+ new-y y-offset)]
                 :when (and (<= 0 x width)
                            (<= 0 y height)
                            (or (= x-offset y-offset 0)
                                (not= x-offset y-offset)))]
             (let [^Color color (if (= x-offset y-offset 0)
                                  particle-color
                                  neighbor-color)]
               (doto graphics
                 (.setColor color)
                 (.drawLine x y x y)))))
          (when (< n num-particles)
            (recur (inc n) new-x new-y)))))
    
    (defn- painter []
      (proxy [JPanel] []
        (paint [^Graphics graphics]
          (let [^int width (proxy-super getWidth)
                ^int height (proxy-super getHeight)]
            (doto graphics
              (.setRenderingHint RenderingHints/KEY_ANTIALIASING
                                 RenderingHints/VALUE_ANTIALIAS_ON)
              (.setRenderingHint RenderingHints/KEY_INTERPOLATION
                                 RenderingHints/VALUE_INTERPOLATION_BICUBIC)
              (.setColor (Color. 135 206 250))
              (.fillRect 0 0 width height))
            (paint-clouds graphics)))))
    
    (defn- gen []
      (let [^JPanel painting-panel (painter)
            ^Dimension dim (Dimension. width height)]
        (doto painting-panel
          (.setSize dim)
          (.setPreferredSize dim))
        (if output-image?
          (let [^BufferedImage bi (BufferedImage. width
                                                  height
                                                  BufferedImage/TYPE_INT_ARGB)
                ^Graphics graphics (.createGraphics bi)]
            (.paint painting-panel graphics)
            (ImageIO/write bi "png" (File. (str "output/"
                                                (System/currentTimeMillis)
                                                ".png"))))
          (let [^JFrame frame (JFrame. "clouds")]
            (.add (.getContentPane frame) painting-panel)
            (doto frame
              (.pack)
              (.setVisible true))))))
    
    (defn -main
      [& args]
      (gen))
    

  • Getting average motorcycle price across all Craigslist cities

    Today I’m going to look at a motorcycle that’s for sale on Craigslist. The asking price for the bike seems fair, but I wanted to get a sense for what other people were asking for the same model and year.

    First I did a local search for the motorcycle I was interested in using the year, make and model search filters. The resultant URL was

    https://philadelphia.craigslist.org/search/mcy?srchType=T&auto_make_model=suzuki+TU250X&min_auto_year=2012&max_auto_year=2012
    

    This returned all the listings in Philadelphia for a 2012 Suzuki TU250X. The srchType=T parameter filters to only include results that have a match in the listing title.

    Using pup, a command-line tool for parsing HTML, I extracted the asking price of the motorcycle in the search result listing.

    curl -s "https://philadelphia.craigslist.org/search/mcy?srchType=T&auto_make_model=suzuki+TU250X&min_auto_year=2012&max_auto_year=2012" | \
    pup 'ul.rows li.result-row p.result-info span.result-meta span.result-price text{}'
    

    There is a CL page that lists every Craigslist site in the US. I parsed that for each location’s specific URL.

    curl -s "https://geo.craigslist.org/iso/us" | \
    pup 'div.geo-site-list-container a attr{href}'
    

    I combined these

    curl -s "https://geo.craigslist.org/iso/us" | \
    pup 'div.geo-site-list-container a attr{href}' | \
    while read location;
     do curl -s "$location/search/mcy?srchType=T&auto_make_model=suzuki+TU250X&min_auto_year=2012&max_auto_year=2012" | \
     pup 'ul.rows li.result-row p.result-info span.result-meta span.result-price text{}';
    done
    

    which outputs the asking prices…

    $3800
    $2750
    $2800
    $2950
    $3800
    $3750
    $2800
    $2400
    $2750
    $2950
    $2750
    $3800
    $3750
    $2700
    $2400
    ...
    

    I was then able to see how the price of the motorcycle in which I was interested compared to similar bikes throughout the US.


  • Resume

    I just updated my resume – the latest version of it should be viewable here.

    For this iteration of resume-writing I decided to try out the open-source JSON Resume schema and tooling. So far it’s OK. My only complaints are:

    • No markdown export support, despite the docs mentioning it
    • PDF exports are pretty gross
    • Can’t export using a local theme, though there’s an open issue about this

    To get a PDF version of my resume I ended up just writing it in markdown and then using pandoc to generate a PDF from that. I’m wondering if just using markdown and pandoc would be a better option in the long term, but we’ll see. It’s nice having a resume under vcs though :)


  • How to set SQL modes for MySQL in a CircleCI container

    In circle.yml you can add comma-separated sql-modes to /etc/mysql/my.cnf and then restart the mysql service, e.g.:

    database:
      pre:
        - echo "[mysqld]" | sudo tee -a /etc/mysql/my.cnf
        - echo 'sql-mode="ALLOW_INVALID_DATES"' | sudo tee -a /etc/mysql/my.cnf
        - sudo service mysql restart
    

  • OS X Setup Guide

    Recently I got a new work laptop. Today I configured it and documented the process. If I make any changes to these instructions I’ll update them in this Gist.

    Update: I recently had to set up a new computer, so I took the opportunity to automate a lot of this stuff: https://github.com/bi1yeu/new-macos

    Here are the programs and tweaks I first made on a fresh install of OS X 10.11:


    Homebrew / Cask

    $ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    $ brew tap caskroom/cask
    $ brew tap caskroom/versions
    $ brew tap caskroom/fonts
    $ brew tap tldr-pages/tldr
    

    Rearrage PATHs

    This is so that Homebrew-installed tools load instead of built-in ones. git, emacs, etc.

    $ sudo vi /etc/paths
    

    Put /usr/local/bin above /usr/bin in the list. Save file and restart terminal.

    Install with Cask

    $ brew cask install alfred                # https://www.alfredapp.com/
    $ brew cask install flux                  # https://justgetflux.com/
    $ brew cask install font-source-code-pro  # https://github.com/adobe-fonts/source-code-pro
    $ brew cask install google-chrome         # https://www.google.com/chrome/browser/desktop/
    $ brew cask install iterm2-nightly        # http://iterm2.com/
    $ brew cask install java                  # https://java.com/en/download/
    $ brew cask install keepingyouawake       # https://github.com/newmarcel/KeepingYouAwake
    $ brew cask install lunchy                # https://github.com/eddiezane/lunchy
    $ brew cask install middleclick           # http://rouge41.com/labs/
    $ brew cask install osxfuse               # https://osxfuse.github.io/
    $ brew cask install spectacle             # https://www.spectacleapp.com/
    

    Install with Homebrew

    $ brew install aspell                     # http://aspell.net/
    $ brew install git                        # https://git-scm.com/
    $ brew install gnupg2                     # https://www.gnupg.org/
    $ brew install homebrew/fuse/sshfs        # https://github.com/libfuse/sshfs
    $ brew install httpie                     # https://github.com/jkbrzt/httpie
    $ brew install jq                         # https://stedolan.github.io/jq/
    $ brew install leiningen                  # http://leiningen.org/
    $ brew install markdown                   # http://daringfireball.net/projects/markdown/
    $ brew install pandoc                     # http://pandoc.org/
    $ brew install task                       # http://taskwarrior.org/
    $ brew install the_platinum_searcher      # https://github.com/monochromegane/the_platinum_searcher
    $ brew install tldr                       # http://tldr-pages.github.io/
    

    launchctl / lunchy

    build locate database:

    $ lunchy install /System/Library/LaunchDaemons/com.apple.locate.plist
    $ lunchy start com.apple.locate.plist
    

    Create plist to periodically run brew update (replace <username> below):

      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
      <plist version="1.0">
      <dict>
          <key>Label</key>
          <string>com.<username>.brew-update</string>
          <key>ProgramArguments</key>
          <array>
              <string>/usr/local/bin/brew</string>
              <string>update</string>
          </array>
          <key>StartCalendarInterval</key>
          <dict>
            <key>Hour</key>
            <integer>2</integer>
            <key>Minute</key>
            <integer>0</integer>
          </dict>
          <key>RunAtLoad</key>
          <true />
      </dict>
      </plist>
    

    And start it:

    $ lunchy start ~/Library/LaunchAgents/com.<username>.brew-update.plist
    

    Git configuration

    $ git config --global user.name '<your-proper-name>'
    $ git config --global user.email <email-address>
    $ git config --global core.editor 'vim'
    

    Spacemacs

    http://spacemacs.org/

    $ brew install --with-cocoa emacs
    $ ln -s /usr/local/Cellar/emacs/24.5/Emacs.app /Applications
    $ git clone https://github.com/syl20bnr/spacemacs ~/.emacs.d
    $ git clone https://github.com/bi1yeu/dotfiles
    $ cd ~/dotfiles/ && ./link-dotfiles.sh
    $ emacs --daemon && emacsclient -c &
    

    Bash prompt

    $ brew install bash-git-prompt
    

    Add to .bash_profile:

    if [ -f "$(brew --prefix)/opt/bash-git-prompt/share/gitprompt.sh" ]; then
        source "$(brew --prefix)/opt/bash-git-prompt/share/gitprompt.sh"
    fi
    
    GIT_PROMPT_ONLY_IN_REPO=1
    

    Prefs

    • Map Caps Lock key to Control modifier in Keyboard prefs
    • Adjust key repeat in Keyboard prefs
    • Enable Remote Login in Sharing prefs
    • Prevent computer from sleeping automatically when the display is off in Energy Saver prefs
    • Put display to sleep in Mission Control > Hot Corners prefs

    Generate SSH key and add it to ssh-agent

    $ ssh-keygen -t rsa -b 4096 -C "<email-address>"
    $ chmod 700 ~/.ssh && chmod 600 ~/.ssh/*
    $ eval "$(ssh-agent -s)"
    $ ssh-add ~/.ssh/id_rsa
    

    Edit iTerm2 settings to bind alt+arrow keys to move forward/backward

    https://coderwall.com/p/h6yfda/use-and-to-jump-forwards-backwards-words-in-iterm-2-on-os-x