Full Code of brunchboy/afterglow for AI

main bb5dfd1042f7 cached
169 files
223.1 MB
873.1k tokens
203 symbols
1 requests
Download .txt
Showing preview only (3,516K chars total). Download the full file or copy to clipboard to get everything.
Repository: brunchboy/afterglow
Branch: main
Commit: bb5dfd1042f7
Files: 169
Total size: 223.1 MB

Directory structure:
gitextract_k9q7365g/

├── .gitattributes
├── .github/
│   ├── scripts/
│   │   ├── build.sh
│   │   └── build_guide.sh
│   └── workflows/
│       └── uberjar.yml
├── .gitignore
├── .htmltest.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── doc/
│   ├── README.md
│   ├── antora.yml
│   ├── ds.yml
│   ├── embedded.yml
│   ├── github-actions.yml
│   ├── modules/
│   │   └── ROOT/
│   │       ├── assets/
│   │       │   └── source/
│   │       │       ├── Afterglow logo.ai
│   │       │       ├── Blade.psd
│   │       │       ├── Clojure Beam.psd
│   │       │       ├── ColorPalette.psd
│   │       │       ├── ColorPalette2.psd
│   │       │       ├── F3.psd
│   │       │       ├── Launchpad Mini.psd
│   │       │       ├── Novation.psd
│   │       │       ├── OLA Logo.ai
│   │       │       ├── Push-2-Stopped.psd
│   │       │       ├── Push2NoEffects.psd
│   │       │       ├── PushNoEffects.psd
│   │       │       ├── Show Space.psd
│   │       │       └── WeatherSystem.psd
│   │       ├── nav.adoc
│   │       └── pages/
│   │           ├── README.adoc
│   │           ├── color.adoc
│   │           ├── cues.adoc
│   │           ├── effects.adoc
│   │           ├── fixture_definitions.adoc
│   │           ├── launchpad.adoc
│   │           ├── mapping_sync.adoc
│   │           ├── metronomes.adoc
│   │           ├── oscillators.adoc
│   │           ├── parameters.adoc
│   │           ├── push.adoc
│   │           ├── push2.adoc
│   │           ├── rendering_loop.adoc
│   │           ├── show_space.adoc
│   │           └── videos.adoc
│   └── primes.md
├── logs/
│   └── README.txt
├── package.json
├── project.clj
├── resources/
│   ├── afterglow/
│   │   └── readme.txt
│   ├── docs/
│   │   └── docs.md
│   ├── public/
│   │   ├── css/
│   │   │   ├── bootstrap-cyborg.css
│   │   │   ├── bootstrap-slider.css
│   │   │   ├── bootstrap-switch.css
│   │   │   ├── bootstrap-theme.css
│   │   │   ├── bootstrap.css
│   │   │   ├── jquery.minicolors.css
│   │   │   ├── screen.css
│   │   │   ├── show.css
│   │   │   └── web-repl.css
│   │   ├── epl-v10.html
│   │   ├── font-awesome-4.5.0/
│   │   │   ├── HELP-US-OUT.txt
│   │   │   ├── css/
│   │   │   │   └── font-awesome.css
│   │   │   ├── fonts/
│   │   │   │   └── FontAwesome.otf
│   │   │   ├── less/
│   │   │   │   ├── animated.less
│   │   │   │   ├── bordered-pulled.less
│   │   │   │   ├── core.less
│   │   │   │   ├── fixed-width.less
│   │   │   │   ├── font-awesome.less
│   │   │   │   ├── icons.less
│   │   │   │   ├── larger.less
│   │   │   │   ├── list.less
│   │   │   │   ├── mixins.less
│   │   │   │   ├── path.less
│   │   │   │   ├── rotated-flipped.less
│   │   │   │   ├── stacked.less
│   │   │   │   └── variables.less
│   │   │   └── scss/
│   │   │       ├── _animated.scss
│   │   │       ├── _bordered-pulled.scss
│   │   │       ├── _core.scss
│   │   │       ├── _fixed-width.scss
│   │   │       ├── _icons.scss
│   │   │       ├── _larger.scss
│   │   │       ├── _list.scss
│   │   │       ├── _mixins.scss
│   │   │       ├── _path.scss
│   │   │       ├── _rotated-flipped.scss
│   │   │       ├── _stacked.scss
│   │   │       ├── _variables.scss
│   │   │       └── font-awesome.scss
│   │   ├── fonts/
│   │   │   ├── Lekton/
│   │   │   │   └── SIL Open Font License.txt
│   │   │   ├── Open_Sans_Condensed/
│   │   │   │   └── LICENSE.txt
│   │   │   └── Roboto/
│   │   │       └── LICENSE.txt
│   │   ├── js/
│   │   │   ├── BootstrapMenu.js
│   │   │   ├── bootstrap.js
│   │   │   ├── jquery-2.1.4.js
│   │   │   ├── jquery.console.js
│   │   │   ├── jquery.websocket-0.0.1.js
│   │   │   ├── npm.js
│   │   │   ├── show_updates.js
│   │   │   └── web-repl.js
│   │   └── shaders/
│   │       └── vertex.glsl
│   └── templates/
│       ├── about.html
│       ├── base.html
│       ├── console.html
│       ├── cue_grid.html
│       ├── current-scene-fragment.js
│       ├── current-scene.json
│       ├── error.html
│       ├── fixture-definition.clj.template
│       ├── fragment.glsl
│       ├── home.html
│       ├── link_menu.html
│       ├── show.html
│       ├── sync_menu.html
│       └── visualizer.html
├── src/
│   └── afterglow/
│       ├── beyond.clj
│       ├── carabiner.clj
│       ├── channels.clj
│       ├── controllers/
│       │   ├── ableton_push.clj
│       │   ├── ableton_push_2.clj
│       │   ├── color.clj
│       │   ├── launchpad_mini.clj
│       │   ├── launchpad_mk2.clj
│       │   ├── launchpad_pro.clj
│       │   └── tempo.clj
│       ├── controllers.clj
│       ├── core.clj
│       ├── coremidi4j.clj
│       ├── dj_link.clj
│       ├── effects/
│       │   ├── channel.clj
│       │   ├── color.clj
│       │   ├── cues.clj
│       │   ├── dimmer.clj
│       │   ├── fun.clj
│       │   ├── movement.clj
│       │   ├── oscillators.clj
│       │   ├── params.clj
│       │   └── show_variable.clj
│       ├── effects.clj
│       ├── examples.clj
│       ├── fixtures/
│       │   ├── american_dj.clj
│       │   ├── blizzard.clj
│       │   ├── chauvet.clj
│       │   └── qxf.clj
│       ├── fixtures.clj
│       ├── init.clj
│       ├── midi.clj
│       ├── rhythm.clj
│       ├── show.clj
│       ├── show_context.clj
│       ├── shows/
│       │   ├── chris.clj
│       │   ├── sallie.clj
│       │   └── wedding.clj
│       ├── transform.clj
│       ├── util.clj
│       ├── version.clj
│       └── web/
│           ├── handler.clj
│           ├── layout.clj
│           ├── middleware.clj
│           ├── routes/
│           │   ├── home.clj
│           │   ├── show_control.clj
│           │   ├── visualizer.clj
│           │   └── web_repl.clj
│           └── session.clj
└── test/
    └── afterglow/
        ├── core_test.clj
        ├── effects/
        │   └── color_test.clj
        └── effects_test.clj

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
api-doc/* -diff linguist-vendored


================================================
FILE: .github/scripts/build.sh
================================================
#!/bin/bash


# This script is run by GitHub Actions to build the cross-platform uberjar.

# If this is a full release, tweak the project to generate the correct API doc source links.
if [[ $release_snapshot == "false" ]]
then
    # Update codox source link
    prefix="\/github.com\/Deep-Symmetry\/afterglow\/blob\/"
    sourceswap="s/${prefix}main/${prefix}${git_version}/g"
    mv project.clj project.clj.old
    sed "${sourceswap}" project.clj.old > project.clj
    rm -f project.clj.old
fi

# Make sure Antora is installed
npm install

# Now that the project has been tweaked if needed, do the actual build
lein uberjar

# Rename the output jar to where we want it.
mv target/afterglow.jar "./$uberjar_name"


================================================
FILE: .github/scripts/build_guide.sh
================================================
#!/usr/bin/env bash

# This script is run by GitHub Actions to build the
# Antora site hosting the developer guide.

set -e  # Exit if any command fails.

# There is no point in doing this if we lack the SSH key to publish the guide.
if [ "$GUIDE_SSH_KEY" != "" ]; then

    # Set up node dependencies; probably redundant thanks to main workflow, but just in case...
    npm install

    # Build the cloud version of the documentation site. Note that the API docs are already
    # built in, from the stage of building the uberjar.
    npm run hosted-docs

    # Make sure there are no broken links in the versions we care about.
    curl https://htmltest.wjdp.uk | bash
    bin/htmltest

    # Publish the user guide to the right place on the Deep Symmetry web server.
    rsync -avz doc/build/site/ guides@deepsymmetry.org:/var/www/guides/afterglow/

else
    echo "No SSH key present, not building user guide."
fi


================================================
FILE: .github/workflows/uberjar.yml
================================================
name: Create überjar

on:
  push:
    branches:
      - main

env:
  initial_description: |
    :construction: This is pre-release code for people who want to help test [what is going into the next release](https://github.com/Deep-Symmetry/afterglow/blob/master/CHANGELOG.md).

    > Don’t download this if you aren’t comfortable testing code while it is under active development! Instead, look at the [latest release](https:///github.com/Deep-Symmetry/afterglow/releases/latest).

    :calendar: **Ignore the “release date” above!** Because this is a snapshot release, the executables below (you may need to click to expand the Assets) will change frequently—whenever new code is pushed to the project—so you will want to _download the latest version every time you work with one_. The date you see _on each asset row_ shows you when it was last updated. Source code archive assets are not useful for snapshot releases, they will always reflect the state of the project when the snapshots began.

jobs:
  build_uberjar:
    name: Build cross-platform überjar
    runs-on: ubuntu-latest
    if: "!contains(github.event.head_commit.message, '[skip ci]')"

    steps:
    - name: Check out main branch
      uses: actions/checkout@v3

    - name: Install SSH Key
      uses: shimataro/ssh-key-action@v2
      with:
        key: ${{ secrets.GUIDE_SSH_KEY }}
        known_hosts: 'deepsymmetry.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINfnL8k99MCOHLciHb7czxFCCvF3lbmY2ase2VhdvCTN'

    - name: Prepare Java
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'corretto'

    - name: Install clojure tools
      uses: DeLaGuardo/setup-clojure@13.0
      with:
        lein: 2.11.2

    - name: Determine version being built
      uses: Deep-Symmetry/github-version-action@v1
      with:
        tag-var-name: release_tag

    - name: Determine überjar name from git version, and snapshot status
      run: |
        echo "uberjar_name=afterglow-$git_version.jar" >> $GITHUB_ENV
        if [[ $release_tag =~ .*-SNAPSHOT ]]
        then
          echo "release_snapshot=true" >> $GITHUB_ENV
        else
          echo "release_snapshot=false" >> $GITHUB_ENV
        fi

    - name: Cache Leiningen dependencies
      uses: actions/cache@v3
      with:
        path: ~/.m2/repository
        key: ${{ runner.os }}-lein-${{ hashFiles('**/project.clj') }}
        restore-keys: |
          ${{ runner.os }}-lein-

    - name: Install dependencies, forcing updates of snapshots
      run: lein -U deps

    - name: Install antora for building user guide
      run: npm install

    - name: Build überjar
      run: bash .github/scripts/build.sh

    - name: Upload überjar
      if: success()
      uses: Xotl/cool-github-releases@v1
      with:
        mode: update
        tag_name: ${{ env.release_tag }}
        isPrerelease: ${{ env.release_snapshot }}
        replace_assets: ${{ env.release_snapshot }}
        assets: ${{ env.uberjar_name }}
        github_token: ${{ github.token }}
        initial_mrkdwn: ${{ env.initial_description }}

    - name: Cache htmltest results
      uses: actions/cache@v3
      with:
        path: tmp/.htmltest
        key: ${{ runner.os }}-htmltest

    - name: Build and publish user guide
      env:
        GUIDE_SSH_KEY: ${{ secrets.GUIDE_SSH_KEY }}
      run: bash .github/scripts/build_guide.sh


================================================
FILE: .gitignore
================================================
*.class
*.jar
.hg/
.hgignore
/.cider_history
/.dir-locals.el
/.env
/.lein-*
/.nrepl-port
/checkouts
/classes
/doc/build
/gh-pages
/node_modules
/pom.xml
/pom.xml.asc
/research
/target
/resources/afterglow/version.edn
logs/*.log*
.idea/
*.iml
*.ipr
*.iws

# Local Netlify folder
.netlify


================================================
FILE: .htmltest.yml
================================================
DirectoryPath: "doc/build/site"
IgnoreURLs:
- "github.com/Deep-Symmetry/afterglow/blob/"
- "support.native-instruments.com/hc/"
- "help.ableton.com/hc/"
- "^http://localhost:9090"


================================================
FILE: CHANGELOG.md
================================================
# Change Log

All notable changes to this project will be documented in this file.
This change log follows the conventions of
[keepachangelog.com](http://keepachangelog.com/).

## [Unreleased][unreleased]

### Fixed

- Afterglow would crash when told to convert a QLC+ fixture definition
  in the current working directory (with no path component),
  [#72](https://github.com/Deep-Symmetry/afterglow/issues/72).
- External links were not being checked in the developer guide, but
  even so the link checker plugin ended up failing. Switched to
  htmltest, which is used on other Deep Symmetry projects, enabled
  external link checking (although not yet for GitHub source links
  because they are hitting rate limits, there are so many of them),
  and fixed a great many broken/stale links.

### Added

- A new effect builder, `wrap-fade-in-out`, to make it easy to build
  effects that fade in when you start them, and out when you tell them
  to end.
- When resolving dynamic parameters, resolution will be repeated if
  the result was still a dynamic parameter. This allows, for example,
  oscillated parameters to be stored in show variable parameters and
  swapped out during the lifetime of a cue. Resolving the variable
  parameter will first obtain the oscillated parameter, which will be
  resolved again for the appropriate number based on the show
  metronome snapshot.
- The `variable-effect` function now takes optional keyword arguments
  which allow a more meaningful effect name to be specified, and can
  suppress the resolution of dynamic parameters, allowing the raw
  parameter itself to be set into a show variable while the effect is
  active, so that it can be used by other effects.


## [0.2.5] - 2022-01-12

### Fixed

- The built-in converter for QLC+ fixture definitions (`.qxf` files)
  now supports the new XML schema used by QLC+.
- There were some off-by-one errors in the metronome, which canceled
  each other out in the places they were used, but would have caused
  problems in new situations. They were discovered while porting the
  logic to Java for the
  [electro](https://github.com/Deep-Symmetry/electro) library.
- Abrupt tempo changes could cause the metronome to jump to the wrong
  place, even the wrong beat. This is now prevented, leaving you at
  the exact same place in the beat grid before and after any tempo
  change.
- The metronome better handles negative time, and has some improved
  documentation.

### Added

- The detailed documentation has been reorganized as a Developer Guide
  and is now built by [Antora](http://antora.org) to provide easier
  navigation and a more readable presentation.
- The Developer Guide is now available through the built-in web server
  even if you do not have a connection to the internet.

### Changed

- Upgraded to version 2.0 of the Eclipse Publice License.

## [0.2.4] - 2017-04-18

### Fixed

- The pixels of the Blizzard Pixellicious were flipped over the X
  axis.
- The web interface needed to have the Beat Link `DeviceFinder`
  started before trying to render the UI, or it would only partially
  render because of an exception when trying to use it.
- Calling `show/patch-fixture!` returned a huge recursive data
  structure which would cause the REPL to choke trying to print it,
  especially in an editor. Now it just returns the key identifying the
  fixture that was patched.

### Added

- A new command-line argument, `-n`, tells afterglow not to try to
  launch a web browser when it is invoked from the command-line. This
  is to enable headless operation on small servers which lack a
  windowing environment.

## [0.2.3] - 2016-06-09

### Fixed

- Rich controller bindings (Ableton Push and Novation Launchpad
  families) were not being successfully auto-bound under Windows
  because of significant differences in the way their port names were
  assigned on that platform. Fixing that was surprisingly tricky, but
  it has been done. I look forward to someone being able to help test
  this under Linux now.
- The graphical display on the Ableton Push 2 could not be connected
  under Windows due to an issue in the Wayang library. That library
  has been fixed, and the new version is embedded in this release.

## [0.2.2] - 2016-05-30

### Added

- The CoreMIDI4J library is now embedded within Afterglow, so it does
  not need to be separately installed by the user for MIDI to work
  properly on Mac OS X. If you have a separate installation of
  CoreMIDI4J in `/Library/Java/Extensions`, you should remove it to
  avoid version conflicts with newer versions shipped with Afterglow.
- Afterglow now uses the new beat-link library to synchronize with
  Pioneer DJ Link equipment, which enables several new features such
  as tracking the master player, synching to bars as well as beats,
  and having the metronome reflect the current track position.
- Holding down Shift while pressing a scroll arrow on the Launchpad
  family of controllers now moves you as far as possible in that
  direction, as it did on the Push 2.
- Head information is now available to channel effects (which include
  dimmer effects), so they can use dynamic parameters with spatial
  components, just like all the other kinds of effects can.

### Fixed

- When updating large sections of the cue grid colors, we no longer
  send all of them at once, because this was overflowing buffers on
  the Push 2 and losing some updates. Instead we send them in batches,
  ending each batch with a query, and wait for the response so we know
  the controller has caught up.
- Updated to the latest release of the Wayang library for drawing on
  the Push 2 display, which solves an issue that prevented the display
  from working under Window.

### Changed

- The hue and saturation gauges on the Push 2 are now drawn using a
  masking image so they can be anti-aliased to the same outline shape
  as the other gauges, and look much cleaner.
- Boolean gauges on the Push 2 are drawn in red or green for No and
  Yes values, and the transition between values is animated, to make
  it easier to see what is happening, and to relate it to the encoder
  rotation.
- Assigner target IDs can now have arbitrary structure appropriate to
  the needs of the assigner implementation, rather than being forced
  into keywords as they used to be. For the most part, they are
  integers (head IDs), some are tuples (universe ID and channel pairs
  for channel assigners, head ID and function keywords for head
  function assigners, other things for extensions like Beyond laser
  show assigners).


## [0.2.1] - 2016-04-03

### Added

- The web and Push interfaces now offer a way to save adjusted cue
  variable values, so the next time the cue is launched the saved
  values are used.
- Show operators can also create "Macros" by selecting a group of
  running cues and choosing an unused cue grid cell. This will create
  a new compound cue in the cell which will re-run all of the cues
  they specified, with the same parameters they had when the macro was
  created, whenever it is run. The compound cue will end all of its
  component cues when you end it, and will end itself if they end
  independently.
- You can now right-click on cues in the web interface to bring up a
  menu of actions. The menu so far offers just the ability to delete
  the cue if there is one there (this can clean up macros you no
  longer want, for example).
- The Ableton Push 2 is now supported as a rich grid control
  interface, taking full advantage of its color graphic display.
- Support for other members of the Novation Launchpad family of grid
  controllers has been implemented:
  - Launchpad Mini
  - Launchpad S (untested, but the Mini, which works, is based on the S)
  - Launchpad Mk2
- The identity of grid controllers is verified before binding to them,
  by sending a MIDI Device Inquiry message and inspecting the
  response.
- The auto-bind mechanism has been improved so much that the sample
  show can now simply turn it on to fully automate the process of
  detecting and binding to any compatible grid controllers that appear
  in the MIDI environment, with no user or configuration effort.
- Direction and aim parameters can now be transformed by a dynamic
  Java3D `Transform3D` parameter to create kaleidoscopic looks with
  groups of lights.
- A new `confetti` effect which assigns random colors (and optionally
  aim points) to groups of lights at intervals.
- A new `pinstripes` effect which can alternate stripes of color
  across fixtures.
- Incoming MIDI System Exclusive messages can now be received and
  delivered to handlers.
- Cue variables can now be Booleans, to support cues which want to be
  able to adjust the direction of a sawtooth oscillator while running.
- The dimmer oscillator cues created in the sample show now include
  Min and Max variables so the range over which the dimmer oscillates
  can be adjusted.
- The Ableton Push mapping now lets you scroll through all variables
  assigned to a cue so you can see and adjust more than the first two.
- You can now use the touch strip on the Ableton Push to immediately
  jump to any part of the legal value range when adjusting a numeric,
  boolean, or color cue variable, BPM, or the Dimmer Grand Master. The
  LEDs on the touch strip also reflect the current value of the
  variable being adjusted.
- You can use the keyboard arrow keys to navigate around the cue grid
  when using the web UI, as long as no input element is focused.
- You can use the space bar to tap tempo when using the web UI, as
  long as no input element is focused.
- Cues can have visualizer creation functions assigned to them, so
  they can provide animated visualizer displays on the Push 2.

### Fixed

- Fixtures which had no channels assigned to the fixture itself, but
  only to heads (like Blizzard's Pixellicious pixel grids) could not
  be patched properly to shows, because the code checking for address
  conflicts was not able to figure out the universe assigned to them.
- Chases containing only scenes would end instantly rather than
  running, because of some assumptions they were making about how the
  effect protocol was implemented, which scenes violated. Chases are
  now more robust.
- The low-level tempo tap handler was already more useful than I was
  giving it credit for, suitable for both aligning to the current beat
  as well as adjusting the tempo if you hit it three or more times, so
  the shift key can be used to adjust the down beat even on
  unsynchronized shows. This makes it much easier to keep the lights
  in sync manually!
- The color wheel is only applied when a color has sufficient
  saturation for it to make sense. The threshold can be adjusted by
  setting the show variable `:color-wheel-min-saturation`.
- Floating-point cue variables now stay rounded to the specified
  resolution when adjusting them on the Push.
- Effects with Unicode characters in them were crashing the Ableton
  Push display code, since it only handles single byte ASCII plus a
  handful of special symbols used for drawing interface elements. Now
  unprintable characters are substituted with an ellipsis symbol
  rather than crashing.
- The entire Push display was being redrawn on each frame of
  user-interface updates, and all text-labeled button states were
  being set, even if they had not changed from the previous frame.
  These redundant messages are no longer sent, and MIDI messages are
  sent to the Push only when text and button states actually need to
  change.
- The slider tooltips for cue variables in the web UI were getting in
  the way of adjusting the sliders because they would appear when the
  mouse was over the tooltip, not just the slider track. They could
  also not be seen on mobile devices. So they have been turned off
  entirely in favor of always-visible value labels.
- The documentation link in the web interface now takes you to the
  proper version-specific tag of the documentation if it is a release
  build. Snapshot builds take you to `master`.
- The nav bar in the show control web page is now compressed to better
  fit mobile devices, since it can be used on the iPad Pro.
- Extraneous errors were being logged in the browser console because
  we were sometimes returning spurious error responses for cue
  variable updates, saves, and clears.
- The end effect buttons and cue variable scroll buttons under
  the text area on the Ableton Push were not affecting the proper
  effects when the effect view was scrolled back from the most recent.
- The effect overflow indicators in the Push text area were not
  properly disappearing when enough effects ended to render them no
  longer needed until the user pressed Shift to try to scroll.
- Under some circumstances the Push mapping could crash when there was
  no cue associated with an effect.
- All MIDI event handler functions are now called in a context which
  properly recovers from exceptions at the level of that individual
  handler, so other handlers will not be affected.
- Everywhere that Afterglow was checking whether an argument was
  callable as a function has been fixed to use the `ifn?` predicate
  rather than `fn?` since the latter is too restrictive, and only
  returns `true` for functions explicitly created using `(fn ...)`.
  That precluded, for example, the idiomatic Clojure approach of using
  `:x` as a function to extract the _x_ coordinate of a head when
  defining a spatial parameter.

### Changed

- You no longer need to specify what kind of grid controller you are
  trying to bind to in advance; the controller manager in
  `afterglow.controllers` can recognize the supported controllers from
  their responses to the MIDI Device Inquiry message, and instantiate
  the appropriate binding. New controller implementations can register
  themselves when their namespaces are loaded so the controller
  manager will dispatch to them as needed.
- The code to gracefully shut down active controller bindings, which
  was becoming duplicated with every new controller mapping created,
  has been pulled up into the shared controllers namespace.
- The code to watch for and automatically bind to a controller when it
  appears in the MIDI environment has similarly been generalized and
  pulled into the shared controllers namespace.
- The ability to register an interest in all events from a specific
  MIDI device was added, and the controller mapping implementations
  were updated to take advantage of this, so they no longer need to
  receive and filter out all the events from other devices.
- The sample show is becoming a much more practical example of how to
  layer flexible color and dimmer cues, with good cue variables to add
  even more dimensions.
- A lot of repetitive code in the examples namespace was consolidated
  using helper functions.
- The `controllers/IOverlay` protocol was expanded to include the
  ability for an overlay to handle and absorb pitch-bend messages, in
  preparation for supporting the touch strip on the Ableton Push.
- The floating-point format for cue variables was changed from `float`
  to `double` since that is what Clojure actually natively uses.
- All other `float` values which were created throughout Afterglow
  were changed to `double` values, since that is what Clojure actually
  natively uses, and they were getting promoted when used anyway.

## [0.2.0] - 2016-02-02

### Added

- Running effects are now listed on the Show Control page of the Web
  UI, in descending priority order, with buttons to end them, and
  controls to adjust cue parameters, including colors.
- The show's dimmer grand master is now visible and controllable
  below the cue grid in the web UI.
- A rich grid controller mapping for the
  [Novation Launchpad Pro](https://us.novationmusic.com/launch/launchpad-pro#).
- A rich color picker interface on the Ableton Push for cues with
  color variables.
- You can now map sliders and encoders on any MIDI controller to
  adjust components of a color stored in a show variable (that is,
  adjust its red, green, and blue values, or its hue, saturation, and
  lightness).
- The example global color and strobe cues have been upgraded to take
  advantage of the new color cue parameters, so their colors can be
  adjusted on the fly using the web or Push interfaces.
- The Shift button can be used with Tap Tempo buttons on controllers
  and the web UI to set the start of a beat, bar, or phrase, depending
  on the synchronization level of the metronome.
- Buttons or pads on any MIDI controller can now easily be mapped to
  act like the smart Tap Tempo and Shift buttons on the Ableton Push
  and Novation Launchpad.
- Cues can have animated colors on grid controllers including the web
  interface, to help remind operators about the effects they launch,
  or reflect the current value of a color variable on which they are
  based.
- When binding a generic MIDI controller to launch a cue, if that
  controller does not have velocity-sensitive pads, you can assign an
  explicit velocity to use in the binding, for the purpose of setting
  any velocity-sensitive cue variables.
- An explanation of how to bind a MIDI controller to a dimmer master
  has been added to the mapping documentation.
- Animated GIFs in the documentation illustrate how the cue user
  interface works.

### Fixed

- Using dynamic color variables to as inputs for other dynamic color
  parameters could cause crashes when trying to adjust the incoming
  color values due to a longstanding subtle bug, which has been fixed.
- Ordinary MIDI control surfaces were sometimes being mistakenly
  identified as candidate sources of Tratktor beat phase information,
  leading to exceptions in the log file. Now only devices sending
  clock pulses are considered beat phase candidates.
- Cue colors were improved in the web interface and on the Push to
  make it easier to see which cues are active, as well as to make the
  colors more faithful to the cue's intent.
- The text labels on cues in the web interface are now more legible
  because they use a contrasting color based on the cell's perceived
  brightness.
- Incompatible cues are now identified not just from matching effect
  keys, but also from their `:end-keys` lists, so the web and
  controller interfaces provide even more guidance.
- Launching cues from the web interface was not setting any value for
  cue variables configured to be velocity sensitive, which was
  sometimes causing issues. Now the assignment of velocity-adjustable
  variables happens in the process of launching any cue from the grid,
  and a default velocity of 127 is assumed if none is specified.
- The entire frame of a user interface being rendered on a grid
  controller or the web interface now uses the same metronome
  snapshot, to represent a consistent point in time.
- Some testing in a Windows virtual machine revealed issues when
  working with standard Java Midi implementations (as opposed to
  CoreMidi4J on the Mac). These were addressed.

### Changed

- Updated to newly-released Clojure 1.8 for improved performance.
- Deprecated functions were removed:
  - `afterglow.effects.color/find-rgb-heads` (instead use `afterglow.channels/find-rgb-heads`)
  - `afterglow.effects.color/has-rgb-heads?` (instead use `afterglow.channels/has-rgb-heads`)
  - `afterglow.effects.oscillators/sawtooth-beat` (instead use `sawtooth`)
  - `afterglow.effects.oscillators/sawtooth-bar` (instead use `sawtooth` with `:interval :bar`)
  - `afterglow.effects.oscillators/sawtooth-phrase` (instead use `sawtooth` with `:interval :phrase`)
  - `afterglow.effects.oscillators/triangle-beat` (instead use `triangle`)
  - `afterglow.effects.oscillators/triangle-bar` (instead use `triangle` with `:interval :bar`)
  - `afterglow.effects.oscillators/triangle-phrase` (instead use `triangle` with `:interval :phrase`)
  - `afterglow.effects.oscillators/square-beat` (instead use `square`)
  - `afterglow.effects.oscillators/square-bar` (instead use `square` with `:interval :bar`)
  - `afterglow.effects.oscillators/square-phrase` (instead use `square` with `:interval :phrase`)
  - `afterglow.effects.oscillators/sine-beat` (instead use `sine`)
  - `afterglow.effects.oscillators/sine-bar` (instead use `sine` with `:interval :bar`)
  - `afterglow.effects.oscillators/sine-phrase` (instead use `sine` with `:interval :phrase`)
  - `afterglow.effects.params/build-oscillated-param` (instead use
  `afterglow.effects.oscillators/build-oscillated-param`)
  - `afterglow.show/add-midi-control-to-cue-mapping` (instead use
    `afterglow.effects.cues/add-midi-to-cue-mapping`)
  - `afterglow.show/remove-midi-control-to-cue-mapping` (instead use
    `afterglow.effects.cues/remove-midi-to-cue-mapping`)
- The detailed documentation was updated to use attributes to link to the API
  documentation so it could be linked to its release-specific version.
- The API documentation was moved into a github-pages branch so
  versioned snapshots can be kept around.
- A few functions were newly deprecated to improve the consistency of the API:
  - `afterglow.effects.cues/add-midi-control-to-cue-mapping` (instead
    use the more accurately named `add-midi-to-cue-mapping`)
  - `afterglow.effects.cues/remove-midi-control-to-cue-mapping`
    (instead use the more accurately named `remove-midi-to-cue-mapping`)
  - `afterglow.show/remove-midi-control-to-var-mapping`,
    `afterglow.show/remove-midi-control-to-master-mapping`, and
    `afterglow.show/remove-midi-control-metronome-mapping` (they are
    no longer needed, the general-purpose function
    `afterglow.midi/remove-control-mapping` works instead of each)

## [0.1.6] - 2016-01-11

### Added

- Support for [CoreMIDI4J](https://github.com/DerekCook/CoreMidi4J),
  to preferentially use MIDI devices returned by this new lightweight
  open-source Java MIDI service provider implementation for Mac OS X.
  CoreMIDI4J is compatible with current Java and OS versions, and
  addresses long-standing defects in the standard Java MIDI
  implementation, such as support for System Exclusive messages, and
  reconfiguration of the MIDI environment as devices are connected and
  disconnected. Afterglow's MIDI implementation now gracefully handles
  changes in the MIDI environment, cleaning up bindings, synced
  metronomes, grid controllers, and cue feedback functions associated
  with devices which no longer exist, and making new devices available
  for use.
- MIDI device watchers, which can set up bindings whenever a specified
  device is connected. These also allow effortless recovery from a
  temporary disconnection from a device during a show.
- Code cues, making it easy to trigger arbitrary activity from a cue
  grid, [issue 34](https://github.com/Deep-Symmetry/afterglow/issues/34).
- Links to graphs and expanded discussion in the oscillator API docs.
- Dimmer effects can now work with dimmer function ranges on
  multipurpose channels as well as full dedicated dimmer channels.
- Dimmer effects can now also create virtual dimmers for RGB-mixing
  fixtures that don't have any actual dimmer channels, allowing them
  to participate as if they did, by modifying the color effects being
  sent to them.
- Step parameters can now have interval ratios, like oscillators.
- When building step parameters, you can now use dynamic parameters as
  inputs.
- When mapping a MIDI control to a show variable, you can now supply a
  custom function to transform the incoming value into whatever you
  need it to be,
  [issue 32](https://github.com/Deep-Symmetry/afterglow/issues/32).
- When mapping a midi control to launch a cue, if your controller
  supports velocity (and perhaps also aftertouch, or polyphonic key
  pressure), you can have those values affect cue variables which have
  been defined as velocity sensitive, in the same way that Ableton
  Push pads do.
- A variation of the sparkle effect which uses dimmer channels,
  [issue 35](https://github.com/Deep-Symmetry/afterglow/issues/35).
- Some more examples of how to get started working with Afterglow.
- A variety of other documentation improvements.

### Changed

- Oscillators have been completely redesigned in order to be more
  flexible and easy to create and work with, and to support dynamic
  parameters so their configuration can vary over time or location,
  [issue 9](https://github.com/Deep-Symmetry/afterglow/issues/9). The old
  oscillator and oscillated parameter functions have been deprecated,
  and are now stubs wich delegate to the new implementation. They will
  be removed in an upcoming release.
- The functions `add-midi-control-to-cue-mapping` and
  `remove-midi-control-to-cue-mapping` have been moved from the
  `afterglow.show` namespace to `afterglow.effects.cues`, to solve a
  circular dependency conflict which arose in implementing velocity
  and aftertouch support. There are stubs in the old location which
  delegate to the new ones, but they are less efficient than calling
  them in the new location directly, and are deprecated. The stubs
  will be removed in an upcoming release.
- The former `IHeadParam` interface has been eliminated, folding its
  semantics into the `IParam` interface, and simplifying the
  implementation of dynamic parameters,
  [issue 20](https://github.com/Deep-Symmetry/afterglow/issues/20).
- The `:adjust-fn` parameter to `build-variable-param` has been
  renamed `:transform-fn` to be consistent with the equivalent
  mechanism added for MIDI control mappings in
  [issue 32](https://github.com/Deep-Symmetry/afterglow/issues/32). The
  documentation has been improved a bit as well.
- The maps which track MIDI bindings now use the underlying Java
  `MidiDevice` object for their keys, which allows for more efficent
  lookup than the `overtone.midi` `:midi-device` map which was
  previously used.
- The functions which add and remove bindings to MIDI control, note,
  and aftertouch messages have been simplified so they no longer
  require you to come up with a unique keyword to use when later
  removing the binding. Instead, you simply pass the same function
  that was used when establishing the binding to remove it.
- All functions which allow you to select a MIDI device have been made
  consistent, and now allow you to filter devices by a variety of
  criteria, not just the name and description.
- Various maps used to manage Afterglow state, such as shows, cue
  grids, Push controllers and auto-binding watchers, are now tagged
  with type metadata to make it easier to recognize them.

### Fixed

- Clicking on the BPM slider in the web interface now updates the BPM
  (previously you had to actually drag it),
  [issue 18](https://github.com/Deep-Symmetry/afterglow/issues/18).
- Launching `:held` cues from generic MIDI controllers, the Ableton
  Push, and the web interface, would not succeed if the previous
  effect created by the cue was still in the process of ending,
  [issue 33](https://github.com/Deep-Symmetry/afterglow/issues/33).
- Make sure MIDI inputs are connected when `sync-to-midi-clock` is
  called,
  [issue 10](https://github.com/Deep-Symmetry/afterglow/issues/10).
- Also make sure the MIDI inputs are opened when rendering the web UI,
  so that the sync button will be able to list available sources of
  MIDI clock messages.
- Clarified that syncing to Traktor beat phase still requires Traktor
  to be configured to send MIDI clock,
  [issue 37](https://github.com/Deep-Symmetry/afterglow/issues/37).
- Added more detail about how to safely import and configure the
  Afterglow Traktor device mapping.
- A variety of issues ranging from questionable style through misplaced
  documentation, unused or inaccessible code, preconditions that would
  not take effect, and actual problems, were identified by Kibit and
  Eastwood (after discovering how to work around a crash in Eastwood
  caused by the protocol definitions in `rhythm.clj`), were cleaned
  up.

## [0.1.5] - 2015-11-25

### Added

- Chases, which support sequences of effects with a variety of timing,
  fade, and looping options.
- Step parameters, which provide flexible control of chases.
- Pan/Tilt effects, which work at a lower level than direction
  effects, but since they are closer to the physical capabilities of
  the lights, can be helpful in creating natural and intuitive
  movements. They also help avoid issues with geometric singularities
  when fading between different directions.
- Graphs to visually illustrate the available oscillators and the
  parameters that tune their behavior.

### Changed

- Fades now delegate their notion of ending to the underlying effects
  which are being faded between, and pass end requests along to them.
- Stopped embedding `cider-nrepl` because it added too much bloat and
  complexity for an unlikely use case. If you want to work with CIDER
  for live-coding with Afterglow, launch it from a project, rather
  than as an überjar.

### Fixed

- Some MIDI controllers (perhaps those which sent messages on channels
  other than 0?) were causing Overtone's
  [midi-clj](https://github.com/overtone/midi-clj) library to create
  message maps with `nil` values for the `:status` key when sending
  control-change or note messages, which was preventing them from
  being detected or processed correctly. Afterglow now always looks
  for command-like messages via the `:command` key instead,
  [issue 8](https://github.com/Deep-Symmetry/afterglow/issues/8).
- Fading colors in and out from nothing, as represented by a `nil`
  assignment value, was fading to a desaturated version of black,
  which does not lead to the kind of results people generally expect
  and want. In this situation, the color is now faded to or from a
  darkened version of itself.
- The phases of the square-bar and square-phrase oscillators were
  flipped from what they should be according to the documentation, and
  compared with square-beat. This was discovered and corrected when
  graphing them.
- Calculation of white LED channel for colors with lightness less than
  50 was wrong, leading to slight unintentional desaturation of
  colors.
- It was a little too hard to see the difference between the "ready"
  and "active" states for some colors on the Ableton Push after
  introducing full RGB button color support; they are once again more
  visually distinct.
- Preconditions in channel-creation functions for fixture definitions
  were mal-formed, and so were not actually validating the function
  arguments.
- Parts of the introductory walk-through in `README.md` had become
  stale and needed to be updated.
- The API documentation for `patch-fixture!` was fleshed out.

## [0.1.4] - 2015-09-27
### Added
- Support for inverted dimmers (where lower DMX values are brighter).
- Scenes, which allow multiple effects to be grouped into one.
- A framework for fading between effect elements, with sensible
  semantics for colors, aim, directions, and functions, and defaults
  when fading in or out of nothing.
- Fading between entire effects, including complex effects and scenes,
  which do not necessarily affect all the same fixtures and channels.
- A new mechanism for extending the rendering loop to support effects
  which do not result in DMX values to send to the show universes.
- Support for (and examples of) integration with laser shows being run
  by Pangolin Beyond software, using the extension mechanism.
- New conditional effects and variable-setting effects, using the
  extension mechanism.
- A composable effect which can transform the colors being created by
  other effects to build layered looks. The default transformation
  causes the colors to range from fully saturated at the start of each
  beat to pure gray by the end of the beat, but it is very easy to
  swap in other transformations using oscillated parameters.
- Holding down the Shift key while turning the encoder allows the BPM
  to be changed more rapidly (in whole beat increments, rather than
  tenths) on the Ableton Push.
- Fixture definitions for Chauvet LED Techno Strobe, LED Techno Strobe
  RGB, ColorStrip, Spot LED 150, Kinta X, Scorpion Storm FX RGB,
  Scorpion Storm RGX, Q-Spot 160, Intimidator Scan LED 300, Geyser RGB
  fogger, and Hurricane 1800 Flex fogger.
- Example effect which desaturates a rainbow over the course of a
  beat.

### Changed
- Improved readability and parallelism of core rendering loop.
- The default frame rate was raised from 30Hz to 40Hz.
- Ableton Push now uses SysEx message to specify the exact RGB color
  to light up a pad, rather than choosing from the limited set
  available through MIDI velocity.
- Ableton Push now makes sure the pads are put in poly-pressure mode,
  and sets the sensitivity level to reduce the chance of stuck pads.
- The stability of MIDI clock sync was greatly improved, in order to
  facilitate the Beyond integration.
- The refresh rates of the Push and web interfaces were reduced to put
  less load on the CPU.
- The tempo buttons on the Push and web interfaces are now always
  flashed at least once per beat, even if the reduced refresh rate
  causes the normal "on" window to be missed.
- Improved content and format of command-line usage help.

### Fixed
- The Ableton Push binding now ends cues when it receives an afertouch
  value of 0, since the hardware is not reliably sending a note-end
  message, especially when multiple pads are being pressed at once.
- Fail gracefully when trying to bind to an Ableton Push when none can
  be found.
- Some small errors in the documentation were corrected.

## [0.1.3] - 2015-08-16

### Added
- Ability to
  [translate](https://github.com/Deep-Symmetry/afterglow/blob/master/doc/fixture_definitions.adoc#translating-qlc-fixture-definitions)
  fixture definitions from the format used by
  [QLC+](http://www.qlcplus.org/) to help people get started on
  defining fixtures.

### Changed
- Separated OLA communication into its own project,
  [ola-clojure](https://github.com/Deep-Symmetry/ola-clojure#ola-clojure).

## [0.1.2] - 2015-08-09
### Added
- Allow configuration of an alternate host for the OLA daemon
  (primarily for Windows users, since there is not yet a Windows port
  of OLA).
- Flesh out the command-line arguments when running as an executable
  jar.
- Allow a list of files to be loaded at startup when running as an
  executable jar, in order to configure fixtures, shows, effects, and
  cues.
- Support syncing to Traktor’s beat grid with the help of a new custom
  controller mapping.

### Changed
- MIDI sync sources are now always watched for, and can be offered to
  the user without pausing first.

## [0.1.1] - 2015-08-02
### Added
- Ability to register for notification about changes in status of cues
  and values of show variables.
- Creating a show can optionally register it with the web interface by
  passing a description with `:description`.
- Now cleans up thread-local bindings stored by the web REPL when
  sessions time out.

### Changed
- Forked Protobuf related libraries to make them build Java 6
  compatible clases, so
  [afterglow-max](https://github.com/Deep-Symmetry/afterglow-max) can run
  inside the Java environment provided by
  [Cycling ‘74’s Max](https://cycling74.com/).
- Made the meaning of the `:start` attribute of cue variables simpler
  and more consistent.
- Cue variables which respond to aftertouch now also respond to
  initial velocity, and the related configuration attributes have been
  renamed to `:velocity` to reflect this increased generality.
- Improved the detection of project name and version number so they
  work for afterglow-max builds too.

### Fixed
- Eliminated crashes in the Ableton Push interface when trying to
  adjust the value of a cue variable which had not yet been set to
  anything.

## 0.1.0 - 2015-07-19
### Added
- Initial Public Release


[unreleased]: https://github.com/Deep-Symmetry/afterglow/compare/v0.2.5...HEAD
[0.2.5]: https://github.com/Deep-Symmetry/afterglow/compare/v0.2.4...v0.2.5
[0.2.4]: https://github.com/Deep-Symmetry/afterglow/compare/v0.2.3...v0.2.4
[0.2.3]: https://github.com/Deep-Symmetry/afterglow/compare/v0.2.2...v0.2.3
[0.2.2]: https://github.com/Deep-Symmetry/afterglow/compare/v0.2.1...v0.2.2
[0.2.1]: https://github.com/Deep-Symmetry/afterglow/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/Deep-Symmetry/afterglow/compare/v0.1.6...v0.2.0
[0.1.6]: https://github.com/Deep-Symmetry/afterglow/compare/v0.1.5...v0.1.6
[0.1.5]: https://github.com/Deep-Symmetry/afterglow/compare/v0.1.4...v0.1.5
[0.1.4]: https://github.com/Deep-Symmetry/afterglow/compare/v0.1.3...v0.1.4
[0.1.3]: https://github.com/Deep-Symmetry/afterglow/compare/v0.1.2...v0.1.3
[0.1.2]: https://github.com/Deep-Symmetry/afterglow/compare/v0.1.1...v0.1.2
[0.1.1]: https://github.com/Deep-Symmetry/afterglow/compare/v0.1.0...v0.1.1


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at james@deepsymmetry.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

Hi there! We're delighted that you'd like to contribute to this
project. It has been generous collaboration from people all over the
world that has made it possible so far, and your help is key to
keeping it great.

Contributions to this project are [released][contributions-released]
to the public under the [project's open source license](LICENSE).

This project adheres to the
[Contributor Covenant Code of Conduct][covenant].
By participating, you are expected to uphold this code.

## Getting started

Before you can start contributing to Afterglow, you'll need to
set up your environment. Fork and clone the repo and install
[Clojure][clojure] (for compiling and running the code) and
[Leiningen](https://leiningen.org) (to manage the project dependencies
and builds). Both of these rely on having a working Java runtime; if
the mechanism you used to install them did not automatically include
one, I recommend installing a current version of the
[OpenJDK](http://openjdk.java.net).

> Starting with version 0.2.5, you also need to have
> [Antora](https://antora.org) installed because it is used to build
> the embedded copy of the User Guide when building and running from
> source. Assuming you have a current version of `node` installed,
> simply running `npm i` inside the top level directory of the
> afterglow project will install Antora for you. If you don't have a
> node environment and are impatient to get started without getting
> one, you can temporarily comment out the section of `project.clj`
> that builds the user guide. Add `#_` to the `:prep-tasks`
> line so that it looks like this:
>
>  `  :prep-tasks [#_["shell" "npx" "antora" "--fetch" "doc/embedded.yml"`
>
> But keep in mind that if you do this, the built-in user guide will
> not work, and you will not be able to create a release-worthy build
> until you install Antora and restore the line to its un-commented
> state.

Once you have those in place, you can run Afterglow from source by
opening a terminal window inside your clone of the project, and typing
`lein repl`. You will see a bunch of output as the embedded copy of
the User Guide is generated by Antora and the images are copied in,
and then Clojure will start, with output similar to this:

    REPL-y 0.5.1, nREPL 0.8.3
    Clojure 1.10.3
    OpenJDK 64-Bit Server VM 11.0.10+9-LTS
    afterglow loaded.
    afterglow.examples=>

At that point you can follow along with the example flow on the main
project page, and start diving into exploring and changing the source
code.

> :wrench: If you are just building Afterglow to work around the
> current [issue](https://github.com/clojars/clojars-web/issues/195)
> preventing current versions from being available through Clojars, at
> this point you can run `lein install`, which will build and install
> the library in your local Maven repository, so other projects can
> depend on the version that you just built without needing to find it
> on Clojars.

Of course to do any serious work, you will want some sort of editor
with embedded REPL support, and ideally structural editing support for
Lisp s-expressions. We find [GNU Emacs][emacs] with [CIDER][cider] to
be an incredibly productive environment for Clojure work, but some of
our colleagues swear by [IntelliJ IDEA][idea] (even the free Community
Edition) with [Cursive][cursive], so use whatever IDE or editor works
best for you.

For testing you are going to want to install OLA (as described on the
main project page) and have some lighting (and possibly MIDI) hardware
to attach.

## Giving back

Once you have something working you’d like to share, you can open a
[pull request][pulls].

Or if you simply have an idea, or something that you wish worked
differently, feel free to open an [issue][issues] if it seems like
nobody already has.

## Maintainers

Afterglow is primarily maintained by [@brunchboy][brunchboy].

## License

<a href="http://deepsymmetry.org"><img align="right" alt="Deep Symmetry"
 src="doc/modules/ROOT/assets/images/DS-logo-github.png" width="250" height="150"></a>

Copyright © 2016&ndash;2022 [Deep Symmetry, LLC](http://deepsymmetry.org)

Distributed under the
[Eclipse Public License 2.0](https://opensource.org/licenses/EPL-2.0)
By using this software in any fashion, you are agreeing to be bound by
the terms of this license. You must not remove this notice, or any
other, from this software.


[contributions-released]: https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license
[covenant]: http://contributor-covenant.org/
[clojure]: https://clojure.org
[leiningen]: https://leiningen.org
[emacs]: https://www.gnu.org/software/emacs/
[cider]: http://www.cider.mx/en/latest/
[idea]: https://www.jetbrains.com/idea/
[cursive]: https://cursive-ide.com
[pulls]: https://github.com/Deep-Symmetry/afterglow/pulls
[issues]: https://github.com/Deep-Symmetry/afterglow/issues
[brunchboy]: https://github.com/brunchboy


================================================
FILE: LICENSE
================================================
Eclipse Public License - v 2.0

    THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
    PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
    OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.

1. DEFINITIONS

"Contribution" means:

  a) in the case of the initial Contributor, the initial content
     Distributed under this Agreement, and

  b) in the case of each subsequent Contributor:
     i) changes to the Program, and
     ii) additions to the Program;
  where such changes and/or additions to the Program originate from
  and are Distributed by that particular Contributor. A Contribution
  "originates" from a Contributor if it was added to the Program by
  such Contributor itself or anyone acting on such Contributor's behalf.
  Contributions do not include changes or additions to the Program that
  are not Modified Works.

"Contributor" means any person or entity that Distributes the Program.

"Licensed Patents" mean patent claims licensable by a Contributor which
are necessarily infringed by the use or sale of its Contribution alone
or when combined with the Program.

"Program" means the Contributions Distributed in accordance with this
Agreement.

"Recipient" means anyone who receives the Program under this Agreement
or any Secondary License (as applicable), including Contributors.

"Derivative Works" shall mean any work, whether in Source Code or other
form, that is based on (or derived from) the Program and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship.

"Modified Works" shall mean any work in Source Code or other form that
results from an addition to, deletion from, or modification of the
contents of the Program, including, for purposes of clarity any new file
in Source Code form that contains any contents of the Program. Modified
Works shall not include works that contain only declarations,
interfaces, types, classes, structures, or files of the Program solely
in each case in order to link to, bind by name, or subclass the Program
or Modified Works thereof.

"Distribute" means the acts of a) distributing or b) making available
in any manner that enables the transfer of a copy.

"Source Code" means the form of a Program preferred for making
modifications, including but not limited to software source code,
documentation source, and configuration files.

"Secondary License" means either the GNU General Public License,
Version 2.0, or any later versions of that license, including any
exceptions or additional permissions as identified by the initial
Contributor.

2. GRANT OF RIGHTS

  a) Subject to the terms of this Agreement, each Contributor hereby
  grants Recipient a non-exclusive, worldwide, royalty-free copyright
  license to reproduce, prepare Derivative Works of, publicly display,
  publicly perform, Distribute and sublicense the Contribution of such
  Contributor, if any, and such Derivative Works.

  b) Subject to the terms of this Agreement, each Contributor hereby
  grants Recipient a non-exclusive, worldwide, royalty-free patent
  license under Licensed Patents to make, use, sell, offer to sell,
  import and otherwise transfer the Contribution of such Contributor,
  if any, in Source Code or other form. This patent license shall
  apply to the combination of the Contribution and the Program if, at
  the time the Contribution is added by the Contributor, such addition
  of the Contribution causes such combination to be covered by the
  Licensed Patents. The patent license shall not apply to any other
  combinations which include the Contribution. No hardware per se is
  licensed hereunder.

  c) Recipient understands that although each Contributor grants the
  licenses to its Contributions set forth herein, no assurances are
  provided by any Contributor that the Program does not infringe the
  patent or other intellectual property rights of any other entity.
  Each Contributor disclaims any liability to Recipient for claims
  brought by any other entity based on infringement of intellectual
  property rights or otherwise. As a condition to exercising the
  rights and licenses granted hereunder, each Recipient hereby
  assumes sole responsibility to secure any other intellectual
  property rights needed, if any. For example, if a third party
  patent license is required to allow Recipient to Distribute the
  Program, it is Recipient's responsibility to acquire that license
  before distributing the Program.

  d) Each Contributor represents that to its knowledge it has
  sufficient copyright rights in its Contribution, if any, to grant
  the copyright license set forth in this Agreement.

  e) Notwithstanding the terms of any Secondary License, no
  Contributor makes additional grants to any Recipient (other than
  those set forth in this Agreement) as a result of such Recipient's
  receipt of the Program under the terms of a Secondary License
  (if permitted under the terms of Section 3).

3. REQUIREMENTS

3.1 If a Contributor Distributes the Program in any form, then:

  a) the Program must also be made available as Source Code, in
  accordance with section 3.2, and the Contributor must accompany
  the Program with a statement that the Source Code for the Program
  is available under this Agreement, and informs Recipients how to
  obtain it in a reasonable manner on or through a medium customarily
  used for software exchange; and

  b) the Contributor may Distribute the Program under a license
  different than this Agreement, provided that such license:
     i) effectively disclaims on behalf of all other Contributors all
     warranties and conditions, express and implied, including
     warranties or conditions of title and non-infringement, and
     implied warranties or conditions of merchantability and fitness
     for a particular purpose;

     ii) effectively excludes on behalf of all other Contributors all
     liability for damages, including direct, indirect, special,
     incidental and consequential damages, such as lost profits;

     iii) does not attempt to limit or alter the recipients' rights
     in the Source Code under section 3.2; and

     iv) requires any subsequent distribution of the Program by any
     party to be under a license that satisfies the requirements
     of this section 3.

3.2 When the Program is Distributed as Source Code:

  a) it must be made available under this Agreement, or if the
  Program (i) is combined with other material in a separate file or
  files made available under a Secondary License, and (ii) the initial
  Contributor attached to the Source Code the notice described in
  Exhibit A of this Agreement, then the Program may be made available
  under the terms of such Secondary Licenses, and

  b) a copy of this Agreement must be included with each copy of
  the Program.

3.3 Contributors may not remove or alter any copyright, patent,
trademark, attribution notices, disclaimers of warranty, or limitations
of liability ("notices") contained within the Program from any copy of
the Program which they Distribute, provided that Contributors may add
their own appropriate notices.

4. COMMERCIAL DISTRIBUTION

Commercial distributors of software may accept certain responsibilities
with respect to end users, business partners and the like. While this
license is intended to facilitate the commercial use of the Program,
the Contributor who includes the Program in a commercial product
offering should do so in a manner which does not create potential
liability for other Contributors. Therefore, if a Contributor includes
the Program in a commercial product offering, such Contributor
("Commercial Contributor") hereby agrees to defend and indemnify every
other Contributor ("Indemnified Contributor") against any losses,
damages and costs (collectively "Losses") arising from claims, lawsuits
and other legal actions brought by a third party against the Indemnified
Contributor to the extent caused by the acts or omissions of such
Commercial Contributor in connection with its distribution of the Program
in a commercial product offering. The obligations in this section do not
apply to any claims or Losses relating to any actual or alleged
intellectual property infringement. In order to qualify, an Indemnified
Contributor must: a) promptly notify the Commercial Contributor in
writing of such claim, and b) allow the Commercial Contributor to control,
and cooperate with the Commercial Contributor in, the defense and any
related settlement negotiations. The Indemnified Contributor may
participate in any such claim at its own expense.

For example, a Contributor might include the Program in a commercial
product offering, Product X. That Contributor is then a Commercial
Contributor. If that Commercial Contributor then makes performance
claims, or offers warranties related to Product X, those performance
claims and warranties are such Commercial Contributor's responsibility
alone. Under this section, the Commercial Contributor would have to
defend claims against the other Contributors related to those performance
claims and warranties, and if a court requires any other Contributor to
pay any damages as a result, the Commercial Contributor must pay
those damages.

5. NO WARRANTY

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
PURPOSE. Each Recipient is solely responsible for determining the
appropriateness of using and distributing the Program and assumes all
risks associated with its exercise of rights under this Agreement,
including but not limited to the risks and costs of program errors,
compliance with applicable laws, damage to or loss of data, programs
or equipment, and unavailability or interruption of operations.

6. DISCLAIMER OF LIABILITY

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

7. GENERAL

If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this Agreement, and without further
action by the parties hereto, such provision shall be reformed to the
minimum extent necessary to make such provision valid and enforceable.

If Recipient institutes patent litigation against any entity
(including a cross-claim or counterclaim in a lawsuit) alleging that the
Program itself (excluding combinations of the Program with other software
or hardware) infringes such Recipient's patent(s), then such Recipient's
rights granted under Section 2(b) shall terminate as of the date such
litigation is filed.

All Recipient's rights under this Agreement shall terminate if it
fails to comply with any of the material terms or conditions of this
Agreement and does not cure such failure in a reasonable period of
time after becoming aware of such noncompliance. If all Recipient's
rights under this Agreement terminate, Recipient agrees to cease use
and distribution of the Program as soon as reasonably practicable.
However, Recipient's obligations under this Agreement and any licenses
granted by Recipient relating to the Program shall continue and survive.

Everyone is permitted to copy and distribute copies of this Agreement,
but in order to avoid inconsistency the Agreement is copyrighted and
may only be modified in the following manner. The Agreement Steward
reserves the right to publish new versions (including revisions) of
this Agreement from time to time. No one other than the Agreement
Steward has the right to modify this Agreement. The Eclipse Foundation
is the initial Agreement Steward. The Eclipse Foundation may assign the
responsibility to serve as the Agreement Steward to a suitable separate
entity. Each new version of the Agreement will be given a distinguishing
version number. The Program (including Contributions) may always be
Distributed subject to the version of the Agreement under which it was
received. In addition, after a new version of the Agreement is published,
Contributor may elect to Distribute the Program (including its
Contributions) under the new version.

Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
receives no rights or licenses to the intellectual property of any
Contributor under this Agreement, whether expressly, by implication,
estoppel or otherwise. All rights in the Program not expressly granted
under this Agreement are reserved. Nothing in this Agreement is intended
to be enforceable by any entity that is not a Contributor or Recipient.
No third-party beneficiary rights are created under this Agreement.

Exhibit A - Form of Secondary Licenses Notice

"This Source Code may also be made available under the following 
Secondary Licenses when the conditions for such availability set forth 
in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
version(s), and exceptions or additional permissions here}."

  Simply including a copy of this Agreement, including this Exhibit A
  is not sufficient to license the Source Code under Secondary Licenses.

  If it is not possible or desirable to put the notice in a particular
  file, then You may include the notice in a location (such as a LICENSE
  file in a relevant directory) where a recipient would be likely to
  look for such a notice.

  You may add additional accurate notices of copyright ownership.

================================================
FILE: README.md
================================================
# Afterglow

[![project chat](https://img.shields.io/badge/chat-on%20zulip-brightgreen)](https://deep-symmetry.zulipchat.com/#narrow/stream/318697-afterglow)
 <image align="right" width="275" height="255"
 src="doc/modules/ROOT/assets/images/Afterglow-logo-padded-left.png"> <br/><br/> An
 environment supporting
 [live coding](https://en.wikipedia.org/wiki/Live_coding) for the
 creation of algorithmic light shows in [Clojure](http://clojure.org),
 leveraging the
 [Open Lighting Architecture](https://www.openlighting.org/ola/) with
 the help of
 [ola-clojure](https://github.com/Deep-Symmetry/ola-clojure#ola-clojure),
 [wayang](https://github.com/Deep-Symmetry/wayang#wayang),
 [beat-link](https://github.com/Deep-Symmetry/beat-link), and pieces of
 the [Overtone](http://overtone.github.io) toolkit. Beyond building on
 pieces of Overtone, the entire Afterglow project was
 [inspired](https://vimeo.com/22798433) by it.

[![License](https://img.shields.io/badge/License-Eclipse%20Public%20License%202.0-blue.svg)](#license)

### Documentation Overview

This page provides an introduction in how to install and use
Afterglow. The [Developer
Guide](https://afterglow-guide.deepsymmetry.org) goes much deeper,
and there is also [API
documentation](http://afterglow-guide.deepsymmetry.org/api/). For more
interactive help, the [Afterglow stream on
Zulip](https://deep-symmetry.zulipchat.com/#narrow/stream/318697-afterglow) is the place to start,
and if you want to see (or contribute to) more structured and lasting
community-driven documentation, there is also a project
[wiki](https://github.com/Deep-Symmetry/afterglow/wiki).

## Why Explore Afterglow?

> tl;dr&mdash;show me? Check out the
> [Show Control pics](https://afterglow-guide.deepsymmetry.org/afterglow/README.html#show-control)
> and [performance video](https://afterglow-guide.deepsymmetry.org/afterglow/videos.html).

As suggested by the live-coding orientation mentioned above, which is
designed to let you inject your own code right into the frame
rendering process, Afterglow takes a very different approach to
controlling light shows than other software. It won&rsquo;t be right
for everyone, but will be extremely compelling to a particular niche.
The early stages of its [rendering
loop](https://afterglow-guide.deepsymmetry.org/afterglow/rendering_loop.html#the-rendering-loop)
can offer higher levels of abstraction than the usual DMX [channel
value](https://afterglow-guide.deepsymmetry.org/afterglow/effects.html#channel-effects)
or [fixture
function](https://afterglow-guide.deepsymmetry.org/afterglow/effects.html#function-effects)
(although those are fully supported too):

* You can express your desired results in terms of an abstract
  [color](https://afterglow-guide.deepsymmetry.org/afterglow/effects.html#color-effects),
  including support for the hue-saturation-lightness model, which is
  great for algorithmic looks, and have it translated to whatever
  color channels (or color wheel) your fixture supports.

* Groups of moving heads can be told to face particular
  [directions](https://afterglow-guide.deepsymmetry.org/afterglow/effects.html#direction-effects)
  by specifying parameterized vectors, or to
  [aim](https://afterglow-guide.deepsymmetry.org/afterglow/effects.html#aim-effects)
  at a particular point in space, and Afterglow figures out how to
  translate that into DMX control values given its understanding of
  the
  [fixture](https://afterglow-guide.deepsymmetry.org/afterglow/fixture_definitions.html)
  and
  [where](https://afterglow-guide.deepsymmetry.org/afterglow/show_space.html),
  and at what angle, you hung it.

* There are a variety of
  [oscillators](https://afterglow-guide.deepsymmetry.org/afterglow/oscillators.html)
  which can efficiently drive effect parameters.

* You can also create
  [complex effects](https://afterglow-guide.deepsymmetry.org/afterglow/effects.html#complex-effects),
  with
  [adjustable parameters](https://afterglow-guide.deepsymmetry.org/afterglow/parameters.html)
  that can be controlled through a rich binding to an
  [Ableton Push](https://afterglow-guide.deepsymmetry.org/afterglow/push2.html) or
  [Novation Launchpad family](https://afterglow-guide.deepsymmetry.org/afterglow/launchpad.html)
  controller, or via
  [Open Sound Control](https://afterglow-guide.deepsymmetry.org/afterglow/mapping_sync.html#open-sound-control)
  (OSC)&mdash;even wirelessly from a tablet or smartphone.

* The timing of effects is pervasively influenced by a deep notion of
  [musical time](https://afterglow-guide.deepsymmetry.org/afterglow/metronomes.html),
  with support for synchronization via
  [MIDI clock](https://afterglow-guide.deepsymmetry.org/afterglow/mapping_sync.html#syncing-to-midi-clock),
  [Traktor Beat Phase](https://afterglow-guide.deepsymmetry.org/afterglow/mapping_sync.html#syncing-to-traktor-beat-phase),
  or Pioneer
  [Pro DJ Link](https://afterglow-guide.deepsymmetry.org/afterglow/mapping_sync.html#syncing-to-pro-dj-link)
  beat grids.

* You can even host Afterglow within
  [Cycling ‘74’s Max](https://cycling74.com/) visual interactive
  environment.

If any of this sounds interesting to you, read on to see how to get
started!

## Table of Contents

  * [Why Explore Afterglow?](#why-explore-afterglow)
  * [Installation](#installation)
  * [Status](#status)
  * [Getting Help](#getting-help)
  * [Usage](#usage)
  * [Troubleshooting](#troubleshooting)
  * [Bugs](#bugs)
  * [What Next?](#what-next)
  * [Release Checklist](#release-checklist)
  * [Tasks](#tasks)
  * [License](#license)

## Installation

1. [Install OLA](https://www.openlighting.org/ola/getting-started/downloads/).
   (On the Mac I recommend using [Homebrew](http://brew.sh) which lets you simply
   `brew install ola`). Once you launch the `olad` server you can
   interact with its embedded
   [web server](http://localhost:9090/ola.html), which is very helpful
   in seeing whether anything is working; you can even watch live DMX
   values changing.

   > :wrench: If you are installing Afterglow on Windows, see the
   > [Wiki discussion](https://github.com/Deep-Symmetry/afterglow/wiki/Questions#ola-and-windows)
   > about OLA options.

2. For now set up a Clojure project using [Leiningen](http://leiningen.org).

3. Add this project as a dependency:
   [![Clojars Project](https://img.shields.io/clojars/v/afterglow.svg)](http://clojars.org/afterglow)

> :wrench: If you were using older releases of Afterglow and installed
> [CoreMIDI4J](https://github.com/DerekCook/CoreMidi4J) in
> `/Library/Java/Extensions`, you need to remove it, because Afterglow
> now embeds an improved version and uses it when necessary.

If you want to run Afterglow as a standalone executable, you can
download the executable &uuml;berjar from the
[releases](https://github.com/Deep-Symmetry/afterglow/releases) page.
[![&uuml;berjar](https://img.shields.io/github/downloads/Deep-Symmetry/afterglow/total.svg)](https://github.com/Deep-Symmetry/afterglow/releases)

For an example of a project which uses Afterglow as a dependency, as
described above, see
[afterglow-max](https://github.com/Deep-Symmetry/afterglow-max#afterglow-max),
which hosts Afterglow inside [Cycling ‘74’s Max](https://cycling74.com/).

## Status

Although Afterglow is far from finished, it&rsquo;s ready for the world to
start exploring, and helping decide directions in which to grow next
(as well as identifying areas where the documentation needs
clarification or reinforcement).

Most of the crazy ideas have panned out and been implemented, and I am
fleshing out the basic details needed for everyday use. The examples
are starting to be intriguing and informative, and the [Developer
Guide](https://afterglow-guide.deepsymmetry.org) is getting
substantial. The modeling of fixtures, channels, etc. is coming
together nicely, though there may be a few more changes.

There is now an embedded web application, which is growing into a show
control interface for people who are not Clojure hackers, and a useful
adjunct to the Ableton Push and Launchpad family control surface
interfaces. Each is explained in the documentation link above.
Afterglow also includes the beginnings of a show visualizer for
designing and working on effects without having to physically hook up
lights (a proof of concept, really, at this point). This is
implemented in WebGL using a volumetric ray tracer and looks quite
promising, at least for a small number of fixtures; it will probably
overwhelm the graphics processor on most systems once you add too many
lights. However, the framework can be used by someone who actually
knows OpenGL programming to build a more scalable preview (albeit one
that probably doesn’t look quite so photo-realistic with beams
impacting drifting fog). This is an area where I would love some help
if it sounds interesting!

## Getting Help

<a href="http://zulip.com"><img align="right" alt="Zulip logo"
 src="doc/modules/ROOT/assets/images/zulip-icon-circle.svg"
 width="128" height="128"></a>

Deep Symmetry&rsquo;s projects are generously sponsored with hosting by <a
href="https://zulip.com">Zulip</a>, an open-source modern team chat
app designed to keep both live and asynchronous conversations
organized. Thanks to them, you can <a
href="https://deep-symmetry.zulipchat.com/#narrow/stream/318697-afterglow">chat
with our community</a>, ask questions, get inspiration, and share your
own ideas.

## Usage

> The rest of this document primarily provides an introduction to the
> configuration of Afterglow from the command line and text files. The
> show control interface is explained in the
> [web](https://afterglow-guide.deepsymmetry.org/afterglow/README.html#web-ui)
> and
> [Push](https://afterglow-guide.deepsymmetry.org/afterglow/push2.html)
> sections.

Although you will often want to use Afterglow from a Clojure repl, you
can also bring it up as an executable jar, and run it using `java
-jar` with command-line arguments:

```
> java -jar afterglow.jar --help

afterglow 0.2.4, a live-coding environment for light shows.
Usage: afterglow [options] [init-file ...]
  Any init-files specified as arguments will be loaded at startup,
  in the order they are given, before creating any embedded servers.

Options:
  -w, --web-port PORT     16000               Port number for web UI
  -n, --no-browser                            Don't launch web browser
  -o, --osc-port PORT     16001               Port number for OSC server
  -r, --repl-port PORT                        Port number for REPL, if desired
  -l, --log-file PATH     logs/afterglow.log  File into which log is written
  -H, --olad-host HOST    localhost           Host name or address of OLA daemon
  -P, --olad-port PORT    9010                Port number OLA daemon listens on
  -q, --convert-qxf PATH                      Convert QLC+ fixture file and exit
  -h, --help                                  Display help information and exit

If you translate a QLC+ fixture definition file, Afterglow will try to write
its version in the same directory, but won't overwrite an existing file.

If you do not explicitly specify a log file, and Afterglow cannot write to
the default log file path, logging will be silently suppressed.

Please see https://github.com/Deep-Symmetry/afterglow for more information.
```

As noted, you can pass a list of init-files when you run Afterglow
this way, which gives you the opportunity to set up the actual
universes, fixtures, effects, and cues that you want to use in your
show. As a starting point, you could put something like the following
in a file `my-show.clj` and then invoke Afterglow as `java -jar afterglow.jar my-show.clj`:

```clojure
(ns my-show
  "Set up the fixtures, effects, and cues I actually want to use."
  ;; TODO: Your list of required namespaces will differ from this, depending on
  ;;       what fixtures you actually use, and what effects and cues you create.
  (:require [afterglow.core :as core]
            [afterglow.transform :as tf]
            [afterglow.effects.color :refer [color-effect]]
            [afterglow.effects.cues :as cues]
            [afterglow.effects.dimmer :refer [dimmer-effect]]
            [afterglow.effects.fun :as fun]
            [afterglow.effects.movement :as move]
            [afterglow.effects.oscillators :as oscillators]
            [afterglow.effects.params :as params]
            [afterglow.fixtures.blizzard :as blizzard]
            [afterglow.rhythm :as rhythm]
            [afterglow.show :as show]
            [afterglow.show-context :refer :all]
            [com.evocomputing.colors :refer [create-color hue adjust-hue]]
            [taoensso.timbre :as timbre]))

(defonce ^{:doc "Holds my show if it has been created,
  so it can be unregistered if it is being re-created."}
  my-show
  (atom nil))

(defn use-my-show
  "Set up the show on the OLA universes it actually needs."
  []

  ;; Create, or re-create the show. Make it the default show so we don't
  ;; need to wrap everything below in a (with-show sample-show ...) binding.
  (set-default-show!
   (swap! my-show (fn [s]
                    (when s
                      (show/unregister-show s)
                      (with-show s (show/stop!)))
                    ;; TODO: Edit this to list the actual OLA universe(s) that
                    ;;       your show needs to use if they are different than
                    ;;       just universe 1, as below, and change the description
                    ;;       to something descriptive and in your own style:
                    (show/show :universes [1] :description "My Show"))))

  ;; TODO: Replace this to patch in an actual fixture in your show, at its actual
  ;;       universe, DMX address, physical location and orientation, then add all
  ;;       your other fixtures one by one.
  (show/patch-fixture! :torrent-1 (blizzard/torrent-f3) 1 1
                       :x (tf/inches 44) :y (tf/inches 51.75) :z (tf/inches -4.75)
                       :y-rotation (tf/degrees 0))

  ;; Return the show's symbol, rather than the actual map, which gets huge with
  ;; all the expanded, patched fixtures in it.
  '*show*)

(core/init-logging)  ; Log at :info level to rotating files in logs/ subdirectory.
(use-my-show)  ; Set up my show as the default show, using the function above.

;; TODO: Add your custom effects, then assign them to cues with sensible colors
;;       See afterglow.examples for examples.
```

As noted, you will want to look at the
[afterglow.examples](src/afterglow/examples.clj)
namespace for some examples of how to populate this file; the rest of
this section gives an overview and walk-through of how pieces of that
namespace work. The `:require` section at the top of `my-show.clj` is
set up to make it easy to cut and paste from these examples, although
it is not complete, and you will eventually need to learn how to
adjust and optimize it yourself.

> The example code above configures Afterglow to log to a set of
> rotating log files in a `logs/` subdirectory of your project.
> Afterglow will attempt to create that directory if it does not
> exist. If you want to see any logging information, which can be
> quite useful when troubleshooting, you will need to ensure that the
> path to the logs directory is writeable (or that the logs directory
> exists and is writable), otherwise the logging mechanism will
> silently do nothing. The logs will stay out of your way until you
> are interested in them, and take up a limited amount of space, but
> whenever you do want to watch what Afterglow is doing, you can look
> at them, or `tail -f logs/afterglow.log` to watch it live.

As your show gets more complex, you may want to split this into
multiple files, which you can either load by listing them all on the
command line, or by using Clojure&rsquo;s `load-file` function from within
the first file. Or, once you are comfortable with idomatic Clojure
development, by organizing them into a hierarchy of namespaces, and
using the normal `:require` mechanism that is used to pull in
Afterglow&rsquo;s own namespaces.

> :heavy_exclamation_mark: At this early stage of development, using
> Afterglow as an executable jar is less-tested territory, and you may
> find surprising bugs... though this is becoming less of an issue
> since the advent of
> [afterglow-max](https://github.com/Deep-Symmetry/afterglow-max#afterglow-max),
> which is putting Afterglow through its paces as an embedded jar. In
> any case, although the project will gradually evolve into a system
> that non-Clojure hackers can use, for now you are probably best off
> playing with it inside a Clojure development environment, or within
> Max, likely with a Clojure environment connected via nREPL.

Assuming you are using it from within a REPL, there is a namespace
`afterglow.examples` which is intended to help you get started quickly
in exploring the environment, as well as serving as an example of how
to configure your own shows, fixtures, effects, and cues.

> The next two lines are not needed if you are using a checkout of the
> Afterglow source code rather than the library version described
> above, since the project is configured to start you in this
> namespace for convenience.

```clojure
(require 'afterglow.examples)
(in-ns 'afterglow.examples)
```

When you run Afterglow as an executable jar, it will automatically
open a web browser window on its embedded web interface. If you are
using it in another way, you can bring up the web interface, and open
a browser window on it, with a one-liner like this (the first argument
specifies the port on which to run the web interface, and the second
controls whether a browser window should be automatically opened):

```clojure
(core/start-web-server 16000 true)
```

<img alt="Web Interface"
src="doc/modules/ROOT/assets/images/WebHome.png" width="537"
height="427">

As noted at the bottom, the web interface provides a minimal console
as well, so if you are running Afterglow from a jar and just want to
tweak something quickly, you can use that:

<img alt="Web Console"
src="doc/modules/ROOT/assets/images/Console.png" width="850"
height="690">

> However, this does not offer the valuable support you would have
> from a dedicated REPL like
> [Cider](https://github.com/clojure-emacs/cider) (in Emacs) or
> [Cursive](https://cursiveclojure.com) (in IntelliJ): things like
> symbol completion, popup documentation, and command-line recall,
> which make for a vastly more productive exploration session. So even
> when you are running from a jar rather than launching from a REPL,
> you will often want to access a real REPL. You can accomplish that
> with command-line arguments or by using the web console to invoke
> [core/start-nrepl](http://deepsymmetry.org/afterglow/api-doc/afterglow.core.html#var-start-nrepl)
> and then connecting your favorite REPL environment to the network
> REPL port you created.

The web interface does provide a nice show control page, though, with
access to a scrollable grid of cues, and the ability to track the cues
displayed on a physical cue grid control surface like the Ableton Push
or a current member of the Novation Launchpad family, so you can
control them from either place, and see the names that go with the
colored buttons on the control surface. This animated GIF shows how
cues respond to clicks, lightening while they run, and darkening any
cues which cannot run at the same time. It also shows how you can
scroll around a larger grid than fits on the screen at one time
(although it has reduced colors, frame rate, and quality when compared
to the actual web interface):

<img alt="Show Control"
src="doc/modules/ROOT/assets/images/ShowGrid.gif" width="998"
height="912">

Here is the Ableton Push interface tied to the same cue grid. This
physical control surface lets you trigger more than one cue at the
same time, and also gives you niceties unavailable with a mouse, like
pressure sensitivity so your effect intensity, speed, color, or other
parameters can be varied as you alter the pressure which you are
applying to the pads:

<img alt="Push Interface"
src="doc/modules/ROOT/assets/images/GrandMaster2.jpg" width="1200"
height="978">

You can adjust running effects, scroll around the cue grid, and adjust
or sync the show metronome from either interface. Other MIDI
controllers can be mapped to provide similar functionality, and
hopefully such mappings will make their way into Afterglow soon
(indeed, the current Novation Launchpad family is now supported too).
The Afterglow mappings are done entirely on the User layer as well, so
they coexist gracefully with Ableton Live, and you can switch back and
forth by pressing the User button if you want to perform with both.

But, getting back to our REPL-based example: We next start the sample
show, which runs on DMX universe 1. You will want to have OLA
configured to at least have an dummy universe with that ID so you can
watch the DMX values using its web interface. It would be even better
if you had an actual DMX interface hooked up, and changed the show to
include some real lights you have connected. Either way, here is how
you start the show sending control signals to lights:

```clojure
(use-sample-show) ; Create the sample show that uses universe 1.
(show/start!)     ; Start sending its DMX frames.
```

The `afterglow.examples` namespace includes a helper function,
`fiat-lux`, to assign a nice cool blue color to all lights in the
sample show, set their dimmers to full, and open the shutters of the
Torrent moving-head spots, which can be called like this:

```clojure
(fiat-lux)
```

So if you happened to have the same fixtures hooked up, assigned the
same DMX addresses as I did when I wrote this, you would see a bunch
of blue light. More realistically, you can navigate to the `olad`
embedded [web server](http://localhost:9090/new/) and see the
non-zero DMX values in the blue and dimmer channels, assuming you have
set up a Universe with ID 1.

> In an environment where you are running multiple shows, the more
> general way of working with one would look like:

```clojure
(def another-show (some-function-that-creates-a-show))
(with-show another-show
  (show/start!)
  (fiat-lux))
```

> However, the `examples` namespace assumes you are just using one,
> and has set it up as the default show, like this:

```clojure
(set-default-show! sample-show)
```

> That saves us the trouble of wrapping all our show manipulation
> functions inside of `(with-show ...)` to establish a context. You
> will likely want to do something similar in setting up your own
> shows, since a single show is the most common scenario. See the
> `afterglow.show-context`
> [API documentation](http://deepsymmetry.org/afterglow/api-doc/afterglow.show-context.html)
> for more details. The `show-context` namespace also defines the
> dynamic variable `*show*` which you can use to refer to the current
> default show when you need to mention it explicitly, as you will see
> in some of the examples below.

The actual content of `fiat-lux` is quite simple, creating
three effects to achieve the goals mentioned above:

```clojure
(defn fiat-lux
  "Start simple with a cool blue color from all the lights."
  []
  (show/add-effect! :color (global-color-effect "slateblue"
                    :include-color-wheels? true))
  (show/add-effect! :dimmers (global-dimmer-effect 255))
  (show/add-effect! :torrent-shutter
                    (afterglow.effects.channel/function-effect
                    "Torrents Open" :shutter-open 50
                    (show/fixtures-named "torrent"))))
```

We can make the lights a little dimmer...

```clojure
(show/add-effect! :dimmers (global-dimmer-effect 200))
```

> Adding a function with the same keyword as an existing function
> replaces the old one. The dimmer channels drop from 255 to 200.

But for dimmer channels, there is an even better way of doing that:

```clojure
(master-set-level (:grand-master *show*) 80)
```

All cues which set dimmer levels are tied to a dimmer master chain.
If none is specified when creating the cue, they are tied directly
to the show’s dimmer grand master. Setting this to a value less than
100 scales the dimmer values sent to the lights down by that amount.
So the above command dims the lights to 80% of their possible
brightness, no matter what else the cues are trying to do. See the
[dimmer effects API documentation](http://deepsymmetry.org/afterglow/api-doc/afterglow.effects.dimmer.html)
for more details. Here is an example of what I call right away when
testing effects in my office with the little Korg nanoKONTROL 2
plugged in:

```clojure
(show/add-midi-control-to-master-mapping "slider" 0 7)
```

And then the last fader acts as my grand master dimmer, and I can
quickly get relief from overly bright lights. (In a real performance
context, you would want to use [this alternate
approach](https://afterglow-guide.deepsymmetry.org/afterglow/mapping_sync.html#automatic-bindings)
to automatically set up your bindings whenever the controller is
connected. That way, if someone trips over the controller cable, as
soon as you plug it back in, you are good to go again.)

> If you have an Ableton Push, it is even easier to have [intutive
> control](https://afterglow-guide.deepsymmetry.org/afterglow/push2.html#show-control)
> over your show’s grand master dimmer. As soon as you bind the Push
> to your show, the Push Master encoder is automatically tied to the
> show master dimmer, with nice graphical feedback in the text area.
> Plus you get deep control over the show metronome as well, as shown
> in the photo above. If you called `(use-sample-show)` as discussed
> above, as soon as you connect and power on your Push, Afterglow will
> activate its show control interface.

Moving on, though... we can change the global color to orange:

```clojure
(show/add-effect! :color (global-color-effect :orange))
```

> The color channel values change.

Let’s get a little fancy and ramp the dimmers up on a sawtooth curve each beat:

```clojure
(show/add-effect! :dimmers
                  (global-dimmer-effect (oscillators/build-oscillated-param
                                        (oscillators/sawtooth))))
```

Slow that down a little:

```clojure
(afterglow.rhythm/metro-bpm (:metronome *show*) 70)
```

> If you have a web browser open on
> [your OLA daemon](http://localhost:9090/ola.html)’s DMX monitor for
> Universe 1, you will see the values for channels changing, then
> ramping up quickly, then a little more slowly after you change the
> BPM. OLA 0.9.5 introduced a new, beta web UI based on AngularJS
> which you can access through a small
> [New UI (Beta)](http://localhost:9090/new/) link at the bottom of
> the page. In my experience, it has been completely stable, looks a
> lot better, and is *far* more dynamic and responsive at monitoring
> changing DMX values, and presenting them in an intuitive at-a-glance
> way.

If you can, alter the example to use a universe and channels that you
will actually be able to see with a connected fixture, and watch
Clojure seize control of your lights!

### Further Experiments

If you have DJ software or a mixer sending you MIDI clock data, you
can sync the show’s BPM to it (see the [Developer
Guide](https://afterglow-guide.deepsymmetry.org/afterglow/mapping_sync.html#syncing-to-midi-clock)
for details, and for a Traktor controller mapping file that lets you
sync to its beat phase information as well):

```clojure
(show/sync-to-external-clock (afterglow.midi/sync-to-midi-clock "traktor"))
```

How about a nice cycling rainbow color fade?

```clojure
(def hue-param (oscillators/build-oscillated-param (oscillators/sawtooth :interval :bar)
                                                   :max 360))
(show/add-effect! :color (global-color-effect
  (params/build-color-param :s 100 :l 50 :h hue-param)))
```

Or, if you need to be woken up a bit,

```clojure
(show/add-effect! :strobe (afterglow.effects.channel/function-cue
  "Fast blast!" :strobe 100 (show/all-fixtures)))
```

> The [Developer Guide](https://afterglow-guide.deepsymmetry.org) has
> more examples of [building
> effects](https://afterglow-guide.deepsymmetry.org/afterglow/effects.html#effect-examples),
> and [mapping
> parameters](https://afterglow-guide.deepsymmetry.org/afterglow/mapping_sync.html)
> to MIDI controllers. There is also low-level [API
> documentation](http://deepsymmetry.org/afterglow/api-doc), but the
> project documentation is the best starting point for a conceptual
> overview and introduction.

When you are all done, you can terminate the effect handler thread...

```clojure
(show/stop!)
```

And darken the universe you were playing with.

```clojure
(show/blackout-show)
```

> An alternate way of accomplishing those last two steps would have
> been to call `(show/clear-effects!)` before `(show/stop!)` because
> once there were were no active effects, all the DMX values would
> settle back at zero and stay there until you stopped the show.

## Troubleshooting

When afterglow has important events to report, or encounters problems,
it writes log entries. In its default configuration, it tries to write
to a `logs` directory located in the current working directory from
which it was run. If that directory does not exist, and you have not
explicitly configured a path to a log file, it assumes you are not
interested in the logs, and silently suppresses them. So if things are
not going right, the first step is to enable logging. You can either
do this by creating a `logs` folder for Afterglow to use, or by
running it with the `-l` command-line argument to set an explicit log
file path, as described in the [Usage](#usage) section above. If you
do that, afterglow will create any missing directories in the log file
path, and fail with a clear error message if it is unable to log to
the place you asked it to.

The Open Lighting Architecture&rsquo;s
[web interface](http://localhost:9090/new/#/), which you can find on
port 9090 of the machine running afterglow if you installed it in the
normal way, can be useful in troubleshooting as well. You can see if
the universes that afterglow is expecting to interact with actually
exist, are configured to talk to the lighting interfaces you expect,
and are sending DMX channel values that seem reasonable.

## Bugs

Although there are none known as of the time of this release, I am
sure some will be found, especially if you are tracking the master
branch to keep up with the current rapid pace of development. Please
feel free to log
[issues](https://github.com/Deep-Symmetry/afterglow/issues) as you
encounter them!

## What Next?

Everything beyond this point in this document is written for people
who are working on enhancing Afterglow itself.

If you are trying to learn how to use it, jump to the main [Developer
Guide](https://afterglow-guide.deepsymmetry.org) page now!

## Release Checklist

Here is the set of tasks needed to cut a new release:

### Prerelease Steps

- [ ] Check over the documentation. If any moving screen captures are
  needed, see this [gist](https://gist.github.com/dergachev/4627207).
  The command I have used so far is: `ffmpeg -i ~/Desktop/Cues.mov
  -pix_fmt rgb24 -r 10 -f gif - | gifsicle --optimize=3 --delay=10 >
  ~/Desktop/Cues.gif`
- [ ] Update [`CHANGELOG.md`](CHANGELOG.md) to reflect the release:
  make sure nothing is missing, and rename the sections to reflect the
  fact that the unreleased code is now released, and there is nothing
  unreleased.
- [ ] Tag the repository with a non -SNAPSHOT version.

### Release Steps

- [ ] Commit everything including the rebuilt API docs, tag the commit
  with the tag name used above, and push including the tag. `git
  commit -a`, `git tag -a v0.2.0 -m "Release 0.2.0"`, `git push --tags`.
- [ ] Deploy the release to Clojars: `lein deploy clojars`.

### Postrelease Steps

- [ ] Update [`CHANGELOG.md`](CHANGELOG.md) to include a new
  unreleased section.
- [ ] Tag the repository with a new -SNAPSHOT version.
- [ ] Commit and push.

## Tasks

To a large extent, this is now historical, and issue and enhancement
tracking has moved to the
[issues](https://github.com/Deep-Symmetry/afterglow/issues) system. There
are still some interesting ideas here for longer-term consideration,
though.

- [x] Sync metronomes to MIDI
- [x] Add metronome chase for clear sync testing
- [x] Allow parameterized effects functions
- [x] Start wiki
- [x] Allow metronomes to be show variables
- [x] Improve Oscillators
  - [x] Use keyword parameters
  - [x] Add phrase oscillators
  - [x] Finish wiki page
- [x] Migrate wiki documentation into project documentation.
- [x] Have metronome cue take metronome parameter and support dynamic
  parameters.
- [x] Consider having patched fixture hold a reference to the show.
  That way we could stop having to pass it so many places, though it
  would make printing fixtures less useful. (Not needed; dynamic
  binding works better.)
- [x] Add support for named fixture functions which exist as a value
  range subset of a channel, and effects which set them to particular
  values.
- [x] Allow scaling of named fixture functions, for example to allow a
  strobe effect to be set to a rough Hz value despite differences in
  fixture implementation.
- [x] Add color wheel support.
- [x] Review existing fixture definitions for consistency of function
  names, start a style guide in the docs for others creating fixture
  definitions.
- [x] Add configuration support for running OLA on a different machine.
- [ ] Make pass over all source, flesh out API doc and preconditions.
- [ ] Sparkle effect, essentially a particle generator with
  configurable maximum brightness, fade time, distribution.
  - [x] Get basic effect working until spatial features are available.
  - [ ] Work both with arbitrary head list, and with spatially mapped origin/density.
  - [ ] Work as single intensity, or spatially mapped hue/saturation patterns.
- [x] Implement a grand master dimmer in the show which imposes a
    ceiling on all dimmer cues.
  - [x] Also allow custom sub-master dimmer variables, chained off
    each other and ultimately the grand master, assigned to cues. Each
    step can scale the output.
  - [x] All dimmer cues are assigned a master chain, defaulting to the
    grand master if none supplied.
- [x] Consider implementing virtual dimmer effects to allow fixtures
  which lack actual dimmer channels to simulate them and participate
  in the dimmer master chain, as proposed
  [here](https://github.com/Deep-Symmetry/afterglow/issues/43).
- [x] Get geometry engine and head-movement cues working.
- [x] Named cues: Define cues with a unique name so they can have
  parameters saved for them, to be reloaded on future runs, once we
  have a database. Also useful for compound cues, see below.
  - [x] This requires cues to have a mechanism for reporting their
    current variable values for saving, and to look them up when
    saved.
- [ ] Compound cues:
  - [x] Unflattened compound cues trigger multiple cues&rsquo; effects:
    - [x] Each effect gets its own priority, parameters.
    - [x] The compound finishes when all triggered effects do.
    - [x] Implement by having the outer cue&rsquo;s effect call
      `show/add-effect-from-cue-grid!` to launch all the nested
      effects, recording their IDs. The effect will never return any
      assigners, but will report that it has ended when all of the
      nested effects have ended. Telling this effect to end will, in
      turn, call `show/end-effect!` on all nested cues (passing their
      recorded id values as `:when-id`, to avoid inadvertently killing
      later effects run under the same key).
    - [x] Add a `:variable-overrides` parameter to
      `show/add-effect-from-cue-grid!` so compound cues can use it to
      customize the values of parameters introduced by nested cues.
  - [ ] Flattened compound cues flatten their nested cues&rsquo; effects
    into a single new effect:
    - [ ] This effect gets assigned a new priority.
    - [ ] The compound cue aggregates nested cue variables into one
      big list, and passes them down to the nested effects. (Renaming
      with numeric suffixes as needed to avoid name clashes? No, they
      might be shared.)
  - [ ] Compound cues created from solely named cues can be saved and
    restored, so they can be built using the web (and rich controller)
    interface out of existing cues, and current parameter values for
    running cues.
    - [ ] When creating a compound cue this way, will need to check
      for and prevent circular definitions, as well as reporting
      sensible errors when constituent cues can no longer be found.
- [x] Compound effects:
  - [x] Simplest compound effect just delegates to nested effects,
    returning concatenated assigners. But implement as a fade with
    time zero?
  - [x] Fade compound effect: fade in at start, out at end.
  - [x] Cue list compound effect: Move through list of embedded
    effects, with optional fades. Loops, driven by metronome, or a
    variable parameter (knob controls where in the list we are). Maybe
    different implementations?
  - [x] Have effects pass a context map to children with show,
    snapshot, own stuff? For example, so the children can be aware of
    build, duration, a shared palette, other things? Or better, since
    snapshot is already passed, just add a usually-nil section which
    contains information about context, with at least information
    about when overall effect started; current fade level of this
    effect, fading in or out, when effect will end. Even better: This
    can be assoc-ed on to the snapshot, without changing the
    definition in rhythm.clj, since Clojure records are also maps!
    _This seems to be unnecessary given how fades and chases ended up
    being actually implemented._
  - [x] Effects which can do their own blending implement an
    additional interface. Otherwise the fade and chase effects (if
    they are different), handle it. To fade between direction effects,
    convert them to pan/tilt numbers, scale between those, then
    convert back to a direction. _This was implemented as a
    multimethod for each of the effect assigner types._
- [ ] Provide a mechanism for creating and controlling/monitoring
  effects via OSC messages. Probably essentially a special-purpose OSC
  REPL.
- [x] Add web page for viewing/adjusting cue variables; associate
  metadata with the variables so the page can provide appropriate
  editing tools and validation. Values live-update when controllers
  change them.
- [ ] When it comes time to save data and settings, the luminus
  approach looks good, including
  [yesql](https://yobriefca.se/blog/2014/11/25/yesql-sql-in-sql-in-clojure/)
  and h2 as the database. Need to figure out where it would be stored,
  though.
- [x] See if I can get Traktor to just send beat notes for master
  track; if so, add mode for MIDI sync to ride them like DJ link.
  - [x] See example on page 166 of Traktor Bible; it is close, but I
    want to add a condition that makes sure these pulses are sent only
    for the deck which is currently the tempo master. Write to the
    author for advice? Alternately, send separate messages when each
    deck is set as the tempo master, and use those to keep track of
    which beat pulses to pay attention to?
- [ ] See if I can detect which Pro DJ Link device is the current
  master, and if so, add an option for down beat tracking using that.
- [x] Add tap tempo support for really low-end sync.
- [x] Add machine-readable metronome sync status flag, so Push can
  color code it; detect stalled clocks even without stop signals.
- [x] See how Afterglow works as a hosted Max package, with an Inlet
  to send Clojure code to be evaluated, and an outlet for the results.
  This could be a quick way to add beat detection, sound spectrum
  analysis, etc.
  https://pcm.peabody.jhu.edu/~gwright/stdmp/docs/writingmaxexternalsinjava.pdf
  https://docs.cycling74.com/max7/tutorials/jitterchapter51
  https://docs.cycling74.com/max7/vignettes/packages
- [ ] Consider creating a
  [Processing Library](https://github.com/processing/processing/wiki/Library-Overview)
  for Afterglow. They are far more invested in the Java ecosystem than
  Max is, anyway.
- [x] Provide a way to import QLC+ fixture definitions to help people
  get started.
- [ ] Consider importing [Avolites](http://personalities.avolites.com)
  personalities/fixture definitions (D4 files); they seem fairly
  straightforward too.
- [x] Separate, or at least document clearly, how to use the low-level
  OLA communication tools, for the benefit of people interested in
  their own implementations.
- [x] Support Push version 2 if possible. As of March, 2016 this is
  looking possible! Ableton has published
  [detailed documentation](https://github.com/Ableton/push-interface)
  of the MIDI and USB interfaces of the Push 2 and display.
  - [x] For the display, I will need [Libusb](http://libusb.info/),
    and there is a promising-looking Java wrapper,
    [usb4Java](http://usb4java.org). Although there is a start at
    [Clojure bindings](https://github.com/aamedina/tools.usb), it does
    not seem to have gotten far.
  - [x] Embed some [fonts](https://www.google.com/fonts#ReviewPlace:refine/Collection:Roboto|Open+Sans+Condensed:300|Source+Sans+Pro:400,200italic).
- [x] Support the Novation Launchpad series. The Pro has pressure
  sensitivity, so start there. They also provide excellent programmer
  documentation, so it will even be straightforward. For example,
  [Launchpad Pro Programmers Reference Guide](http://global.novationmusic.com/sites/default/files/novation/downloads/10598/launchpad-pro-programmers-reference-guide_0.pdf),
  found on their
  [Downloads Page](http://global.novationmusic.com/support/product-downloads?product=Launchpad+Pro).
  Having someone loan me one would speed this up!
  - [x] Novation is loaning me a Launchpad Mini! It
    [seems](http://www.soundonsound.com/sos/jan14/articles/novation-launchpad.htm)
    to use the same control messages as the Launchpad S, so refer to
    that guide in trying to set it up. Has only red/green 2 bit LEDs, so
    don&rsquo;t try to replicate colors from the cue grid.
  - [x] Novation Launchpad Mk2 support added with the testing help of
    [Benjamin Gudehus](https://github.com/hastebrot).

### Ideas

- [x] Model moving head location and position, so they can be panned and aimed in a coordinated way.
  - [x] [Wikipedia](http://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions)
    has the most promising overview of what I need to do.
  - [ ] Use iOS device to help determine orientation of fixture: Hold
    phone upright facing stage from audience perspective to set
    reference attitude; move to match a landmark on the fixture
    (documented in the fixture definition), and have phone use
    [CoreMotion](https://developer.apple.com/library/ios/documentation/CoreMotion/Reference/CMAttitude_Class/index.html#//apple_ref/occ/instm/CMAttitude/multiplyByInverseOfAttitude:)
    `CMAttitude` `multiplyByInverseOfAttitude` to determine the
    difference.
  - [x] The more I investigate, the more it looks like
    [Java3D’s](http://docs.oracle.com/cd/E17802_01/j2se/javase/technologies/desktop/java3d/forDevelopers/J3D_1_3_API/j3dapi/)
    [Transform3D](http://docs.oracle.com/cd/E17802_01/j2se/javase/technologies/desktop/java3d/forDevelopers/J3D_1_3_API/j3dapi/javax/media/j3d/Transform3D.html)
    object is going to handle it for me, which is very convenient, as
    it is already available in Clojure. To combine transformations,
    just multiply them together (with the `mul` method).
  - [x] Use `setEuler` to set a `Transform3D` to a specific set of
    rotation angles.
  - [x] If this leads to accuracy issues or loss of a degree of
    freedom, consider Quaternions, as recommended in
  this [article](http://java.sys-con.com/node/99792).
  - [x] Wow, this may be exactly what I need: Java code for converting
    Quaternions to Euler Angles:
  http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
  The site is in general an amazing reference for the kind of geometry I need to learn.
  - [x] This seems to be the formula I need to figure out the angles
    to send a light to make it face a particular direction (the
    selected, top answer):
    http://stackoverflow.com/questions/1251828/calculate-rotations-to-look-at-a-3d-point
    and transform.clj has an implementation in invert-direction. Now I
    just need to test it with an actual light!
  - [x] Remember that Vector3d has nice methods like angle (calculate
    angle to another Vector3d), length, cross, dot...
- [ ] Render preview animations of a light show using WebGL.
  - [x] This [shader](https://www.shadertoy.com/view/Mlj3W1) looks
    nearly perfect, if I can figure out how to adopt it.
  - [x] Looks like a nice intro to 3D, linear algebra, shaders:
    [Making WebGL Dance](http://acko.net/files/fullfrontal/fullfrontal/webglmath/online.html).
    Has interesting references too, such as
    [Interactive 3D Graphics Course](https://www.udacity.com/course/interactive-3d-graphics--cs291),
    Eric Haines, Udacity.com. And the
    [Aerotwist tutorials](https://aerotwist.com/tutorials/), in
    particular Three.js and shaders.
  - [x] Fix the transform of lights into the WebGL shader space;
    currently inconsistent.
  - [ ] See if someone can come up with a more bare bones but scalable preview, probably building a geometry of the light cones instead of ray marching through them.
- [ ] Add a Focus effect type, which is resolved after direction and aim
  effects are; this will allow, for example, fixtures to be annotated
  with functions that map from focal distance to DMX value (the
  Torrents would have two such functions, one for each gobo wheel),
  and those functions could be used by an auto-focus effect which
  would be given geometry information about the planes in the room
  (floor, ceiling, walls, screens), could figure out the distance to
  the nearest one the fixture is pointing at, and automatically
  generate a focus channel value to focus at that distance. A fade
  could be used with an oscillator to bounce back and forth between
  focus on each gobo wheel.
- [x] Use [claypoole](https://clojars.org/com.climate/claypoole) for
  parallelism.
- [ ] Change to core.async for all parallelism, since we are already using it anyway.
- [ ] Add OSC support (probably using
  [Overtone&rsquo;s implementation](https://github.com/rosejn/osc-clj))
  for controller support, and MIDI as well.
- [x] Serious references for color manipulation, but in [Julia](https://github.com/timholy/Color.jl).
- [ ] Absolutely amazing reference on
  [color vision](http://handprint.com/LS/CVS/color.html)! Send him a
  note asking if he knows where I can find an algorithm for using
  arbitrary LEDs to make an HSL color!
  - [ ] Consider an alternate HSI color implementation. It could yield more
  pure/accurate results, but perhaps with less intuitive semantics,
  and definitely lower peak output. Most likely a configurable option?
  See the discussion and code on the
  [SaikoLED blog](http://blog.saikoled.com/post/44677718712/how-to-convert-from-hsi-to-rgb-white).
  And [related discussion](http://blog.saikoled.com/post/43693602826/why-every-led-light-should-be-using-hsi),
  with links to color correction.
- [ ] When it is time to optimize performance, study the
  [type hints](http://clojure.org/java_interop#Java%20Interop-Type%20Hints)
  interop information looks very informative and helpful.
  - [ ] satisfies? seems to take twice as long as instance? so change the preconditions to use instance? where possible
  - [ ] Do a pass through all files with *warn-on-reflection* set to
    true, see what hinting can help. `(set! *warn-on-reflection*
    true)` at top of file.
- [ ] Eventually create a leiningen task that can build a standalone
  jar with Afterglow and a custom show definition file and its
  supporting resources, so developers can easily deploy and share
  shows with non-Clojurists.
- [ ] Consider adding support for metronome synchronization with
  [EspGrid](https://github.com/d0kt0r0/EspGrid).
- [ ] Investigate whether
  [Vamp](https://code.soundsoftware.ac.uk/projects/vamp) (and jVamp)
  would be worthwhile for audio analysis. But adding native components
  is likely to be a hassle.
- [ ] See if [thi-ng/color](https://github.com/thi-ng/color) is a
  better fit.
- [x] Once I release the first version, answer this StackOverflow
  [question](http://stackoverflow.com/questions/9582192/dmx-software-to-control-lights-with-programmable-interface).
  - [x] Also submit a link to
    [TOPLAP](http://toplap.org/contact-page/).
  - [ ] Also post a followup to this
    [article](http://radar.oreilly.com/2015/05/creative-computing-with-clojure.html)
    and reach out to some of the artists themselves. (Sadly too late
    to post a comment on the thread, but I should try contacting the
    artists!)

### References

* Clojure implementation of Protocol Buffers via
  [lein-protobuf](https://github.com/flatland/lein-protobuf) and
  [clojure-protobuf](https://github.com/flatland/clojure-protobuf).
* The incomplete
  [Java OLA client](https://github.com/OpenLightingProject/ola/tree/master/java).
* Making [Animated GIF Screencasts](https://gist.github.com/dergachev/4627207).

### Related Work

- [x] Rich controller support for
  [Ableton Push](https://forum.ableton.com/viewtopic.php?f=55&t=193744)!
  - [x] [Color chart](https://forum.ableton.com/viewtopic.php?f=55&t=192920),
    post a followup if my hue theory pans out.
  - [x] Nice
    [breakdown](http://tai-studio.org/index.php/projects/sound-programming/accessing-abletons-push-device/)
    of button sections.
  - [x] It looks like I can actually specify an RGB color for the buttons using another SysEx:
    [PushPix](https://cycling74.com/wiki/index.php?title=Push_Programming_Oct13_03)
- [ ] Could add basic grid control using the
  [Livid Ohm RGB](http://wiki.lividinstruments.com/wiki/OhmRGB),
  alhough it supports only 7 colors, no pressure sensitivity, and has
  been discontinued, so this is a low priority even though we own one,
  unless/until someone requests it.
- [x] Add a user interface using [Luminus](http://www.luminusweb.net/docs).
- [x] Separate [ola-clojure](https://github.com/Deep-Symmetry/ola-clojure#ola-clojure) into its own project.

## License

<a href="http://deepsymmetry.org"><img align="right" alt="Deep Symmetry"
 src="doc/modules/ROOT/assets/images/DS-logo-github.png" width="250" height="150"></a>

Copyright © 2015-2023 [Deep Symmetry, LLC](http://deepsymmetry.org)

Distributed under the [Eclipse Public License
2.0](https://opensource.org/licenses/EPL-2.0). By using this software
in any fashion, you are agreeing to be bound by the terms of this
license. You must not remove this notice, or any other, from this
software. A copy of the license can be found in
[LICENSE](https://github.com/Deep-Symmetry/afterglow/blob/master/LICENSE)
within this project.

<a href="https://www.netlify.com">
  <img align="right" src="https://www.netlify.com/img/global/badges/netlify-color-accent.svg"/>
</a>

### [Antora](https://antora.org)

Antora is used to build the [Developer
Guide](https://afterglow-guide.deepsymmetry.org), for embedding inside
the application, and hosting on [Netlify](https://www.netlify.com).
Antora is licensed under the [Mozilla Public License Version
2.0](https://www.mozilla.org/en-US/MPL/2.0/) (MPL-2.0).


================================================
FILE: doc/README.md
================================================
# Developer Guide Module

> :mag_right: If you are looking for the online documentation, it has
> [moved](https://afterglow-guide.deepsymmetry.org/) off of
> GitHub to become easier to read and navigate.

Afterglow now uses [Antora](https://antora.org) to build its Developer
Guide. this folder hosts the documentation module and playbooks used
to build it. `embedded.yml` is used to create the self-hosted version
which is served out of Afterglow itself, so it can be used even
without an Internet connection, and `github-actions.yml` is used to build the
[online version](https://afterglow-guide.deepsymmetry.org/) that is
built by GitHub Actions.

The Leiningen project in the root of this repository automatically
invokes Antora to build the embedded version as an early build step.
The online version, which will grow to support multiple released
versions of Afterglow, is built automatically whenever changes are
pushed to the relevant branches on GitHub.


================================================
FILE: doc/antora.yml
================================================
name: afterglow
title: Afterglow
version: ~
display_version: 'main'
prerelease: true
start_page: ROOT:README.adoc
asciidoc:
  attributes:
    icons: font
    experimental: ''
    page-copyright: 2015–2022
    page-pagination: ''
nav:
- modules/ROOT/nav.adoc


================================================
FILE: doc/ds.yml
================================================
site:
  title: Afterglow Developer Guide
  url: https://deepsymmetry.org/afterglow/guide
  start_page: afterglow::README.adoc
content:
  sources:
  - url: https://github.com/Deep-Symmetry/afterglow.git
    branches: [main, guide*]
    tags: va*
    start_path: doc
antora:
  extensions:
  - require: '@antora/lunr-extension'
    index_latest_only: true
asciidoc:
  attributes:
    api-doc: http://deepsymmetry.org/afterglow/api-doc/
    icons: font
    experimental: ''
ui:
  bundle:
    url: https://deepsymmetry.org/media/antora/ui-bundle-3.zip
    snapshot: true


================================================
FILE: doc/embedded.yml
================================================
site:
  title: Afterglow Developer Guide
  url: http:/guide
  start_page: afterglow::README.adoc
content:
  edit_url: false
  sources:
  - url: ./..
    branches: HEAD
    start_path: doc
antora:
  extensions:
  - '@antora/lunr-extension'
asciidoc:
  attributes:
    api-doc: link:../../api-doc/
    icons: font
    experimental: ''
ui:
  bundle:
    url: https://deepsymmetry.org/media/antora/ui-bundle-3-self.zip
    snapshot: true
output:
  dir: ./../target/classes/developer_guide


================================================
FILE: doc/github-actions.yml
================================================
site:
  title: Afterglow Developer Guide
  url: https://afterglow-guide.deepsymmetry.org/
  start_page: afterglow::README.adoc
  robots: allow
  keys:
    google_analytics: G-ZNLRY347Y7
urls:
  redirect_facility: httpd
content:
  sources:
  - url: https://github.com/Deep-Symmetry/afterglow.git
    branches: [main, guide*]
    start_path: doc
antora:
  extensions:
  - require: '@antora/lunr-extension'
    index_latest_only: true
asciidoc:
  attributes:
    api-doc: https://afterglow-guide.deepsymmetry.org/api/
    icons: font
    experimental: ''
ui:
  bundle:
    url: https://deepsymmetry.org/media/antora/ui-bundle-3-self.zip
    snapshot: true


================================================
FILE: doc/modules/ROOT/assets/source/Afterglow logo.ai
================================================
%PDF-1.5
%
1 0 obj
<</Metadata 2 0 R/OCProperties<</D<</ON[5 0 R 31 0 R 81 0 R 131 0 R]/Order 132 0 R/RBGroups[]>>/OCGs[5 0 R 31 0 R 81 0 R 131 0 R]>>/Pages 3 0 R/Type/Catalog>>
endobj
2 0 obj
<</Length 50109/Subtype/XML/Type/Metadata>>stream
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c067 79.157747, 2015/03/30-23:40:42        ">
   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
      <rdf:Description rdf:about=""
            xmlns:dc="http://purl.org/dc/elements/1.1/"
            xmlns:xmp="http://ns.adobe.com/xap/1.0/"
            xmlns:xmpGImg="http://ns.adobe.com/xap/1.0/g/img/"
            xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
            xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#"
            xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
            xmlns:stMfs="http://ns.adobe.com/xap/1.0/sType/ManifestItem#"
            xmlns:illustrator="http://ns.adobe.com/illustrator/1.0/"
            xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/"
            xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#"
            xmlns:xmpG="http://ns.adobe.com/xap/1.0/g/"
            xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
         <dc:format>application/pdf</dc:format>
         <dc:title>
            <rdf:Alt>
               <rdf:li xml:lang="x-default">OLA Logo RGB CS6</rdf:li>
            </rdf:Alt>
         </dc:title>
         <xmp:MetadataDate>2015-07-20T14:20:46-05:00</xmp:MetadataDate>
         <xmp:ModifyDate>2015-07-20T14:20:46-05:00</xmp:ModifyDate>
         <xmp:CreateDate>2015-07-20T12:06:36-05:00</xmp:CreateDate>
         <xmp:CreatorTool>Adobe Illustrator CC 2015 (Macintosh)</xmp:CreatorTool>
         <xmp:Thumbnails>
            <rdf:Alt>
               <rdf:li rdf:parseType="Resource">
                  <xmpGImg:width>256</xmpGImg:width>
                  <xmpGImg:height>136</xmpGImg:height>
                  <xmpGImg:format>JPEG</xmpGImg:format>
                  <xmpGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA&#xA;AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK&#xA;DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f&#xA;Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAiAEAAwER&#xA;AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA&#xA;AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB&#xA;UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE&#xA;1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ&#xA;qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy&#xA;obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp&#xA;0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo&#xA;+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FVk88EELzT&#xA;yLFDGC0krkKqqOpLHYDFXknnL/nKf8pfLbvBBfSa9eJUGLS1EsYPvOzJCR/qM3yxV5Fr3/ObvmKV&#xA;2XQPLdpaJ0V76WS5Y+5WL6uB8qnFWHXn/OXP5zzvyivbO0H8kNpGR/yV9U/jiqEj/wCcrPzwVnLa&#xA;5E4Y1VWsrOi+w4xA/fXFU203/nMb83LVl+sppmoKKBhPbMhNOprBJFucVZ/5b/5zesndI/MvlqSF&#xA;f27rTphL/wAkJhH/AMncVe1eR/zq/LXzqUi0TWoTfv0025/0e6r4LHJTn/sOQxVnGKuxV2KuxV2K&#xA;uxVLPM3/ABwb3/jGf1jJQ5uVov76PveUXH90cyC9ZDmxfVP2sw8rtcDFb3+9HzzUah2+LkmOnds1&#xA;OVxszKtN7ZrcrqszKdN7ZrsrqszIF/uRmH1dXlSvUv7psysPN1uo5PPte/azpNG8lrmIf7ub551O&#xA;jecyJhbds6zROJNf+1mVld92ejYvsDMGXN6vF9L6cznnJdirsVeV/nB/zkL5R/LuN7Ef7lvMrLWP&#xA;SoWAEdRUNcyb+mKbgULHwpvir41/MT84vPvn65Ztd1FhYcuUOlW5MVpHQ1H7oE8yP5nJb3xVhOKt&#xA;gEmg3JxVMbby35hugGttMupVPRkhkK7+4FMujp8kuUT8mByRHMhE/wCCvN3/AFZ7v/kU39Ml+Uy/&#xA;zT8mPjQ7wgLvRtYsxW8sbi2A7zROnv8AtAZXLFOPMEfBmJxPIoPK2TYJBBBoRuCOtcVe1flb/wA5&#xA;TeefKLw2OuO/mLQVopiuHJu4l6fubhqk0/lkqOwK4q+xvIn5heVPPWirq/ly8W5g2W4gb4Z4HIrw&#xA;mj6q34HsSMVZHirsVdirsVSzzN/xwb3/AIxn9YyUOblaL++j73lFx/dHMgvWQ5sX1T9rMPK7XAxW&#xA;9/vR881Godvi5Jjp3bNTlcbMyrTe2a3K6rMynTe2a7K6rMyBf7kZh9XV5Ur1L+6bMrDzdbqOTz7X&#xA;v2s6TRvJa5iH+7m+edTo3nMiYW3bOs0TiTX/ALWZWV33Z6Ni+wMwZc3q8X0vpzOecl2Kvnn/AJyK&#xA;/wCckF8rev5T8oTq/mMjjf6ivF0sgesaVqGn8aiif63RV8a3FxcXM8lxcyvNcTMXlmkYu7uxqzMx&#xA;qSSepOKq+maTqOqXa2mn273Nw37CCtB4seij3OWY8UpmoiyxlMRFl6f5d/JeFQs+vXBkfr9UtzRR&#xA;7PIRU/7Gnzzc4OyRzyH4Bwcmt/ms/wBL8uaDpShdPsIbcj/diqC/0uaufpObTHp4Q+kAOHLJKXMp&#xA;llzB2KuxVItY8j+VtWVvrWnxrK3+74R6UlfHklK/7KuYuXR4p8w2wzzjyLzXzP8Ak/qdir3OjSG/&#xA;t13NuwAuAPanwv8ARQ+2ajUdlSjvD1D7XOxawHaWzz10dHZHUq6khlIoQR1BGakhzE98keevMvkr&#xA;Xodb8v3ZtruP4ZENTFNHUFopkqOaNTp9IoaHFX3t+T35w+X/AMyvL4vLMi11i1CrqulM1Xhc/tp/&#xA;NE37LfQd8VZ/irsVdiqWeZv+ODe/8Yz+sZKHNytF/fR97yi4/ujmQXrIc2L6p+1mHldrgYre/wB6&#xA;Pnmo1Dt8XJMdO7ZqcrjZmVab2zW5XVZmU6b2zXZXVZmQL/cjMPq6vKlepf3TZlYebrdRyefa9+1n&#xA;SaN5LXMQ/wB3N886nRvOZEwtu2dZonEmv/azKyu+7PRsX2BmDLm9Xi+l9OZzzkvFv+clPzt/wFoK&#xA;6LosoHmrVo29BxQm0tz8LXBH85NRH71PahVfC0kkksjSSMXkclndiSxYmpJJ6k4qn3k/ydqHmXUP&#xA;RhrFaREG6uyKqgPYeLHsMytLpZZpUOXUtObMIDze8aB5d0nQrIWmnQiNf92SHeSRv5nbvnT4MEMU&#xA;aiHU5MhmbKZ5cwdiq2SSONGkkYJGgLO7GgAHUknATSpXZebPLV7dfVLTU7ea4JosayCrHwX+b6Mp&#xA;hqccjQkLbJYpAWQm2XtbsVdirEPPH5eaf5hha5twttqyj4JwKLJQbLLT/iXUZgazQxyixtL8c3Iw&#xA;agw2PJ4Xf2F5YXktneRNDcwtxkjbqD/TwOczOBiaOxDtoyBFhN/IvnfXvJXmW08waJN6d3bGjxtX&#xA;05oiRzhlUEckem/3jcA5FL9Evy9896L568qWfmPSG/cXK8Z7diC8E6/3kL0/aU/eKHocVZHirsVS&#xA;zzN/xwb3/jGf1jJQ5uVov76PveUXH90cyC9ZDmxfVP2sw8rtcDFb3+9HzzUah2+LkmOnds1OVxsz&#xA;KtN7ZrcrqszKdN7ZrsrqszIF/uRmH1dXlSvUv7psysPN1uo5PPte/azpNG8lrmIf7ub551OjecyJ&#xA;hbds6zROJNf+1mVld92ejYvsDMGXN6vF9L6I80+ZNM8s+XdR1/VH4WOmwPPMRTkeI2Ra0qztRVHi&#xA;c55yX5t+d/N+recPNOo+Y9Vfld6hKZOANVjjG0cSf5MaAKPliqt5A8kax5382WHlzSl/0i8f95OR&#xA;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</xmpGImg:image>
               </rdf:li>
            </rdf:Alt>
         </xmp:Thumbnails>
         <xmpMM:InstanceID>uuid:179f7641-5b82-f94b-aee1-41c2d263a77c</xmpMM:InstanceID>
         <xmpMM:DocumentID>xmp.did:3f5bc854-b7f3-4530-a63b-4e8e84ed7795</xmpMM:DocumentID>
         <xmpMM:OriginalDocumentID>uuid:5D20892493BFDB11914A8590D31508C8</xmpMM:OriginalDocumentID>
         <xmpMM:RenditionClass>proof:pdf</xmpMM:RenditionClass>
         <xmpMM:DerivedFrom rdf:parseType="Resource">
            <stRef:instanceID>uuid:4b8d80fb-79e0-e648-b7e5-561a697a3c5f</stRef:instanceID>
            <stRef:documentID>xmp.did:78407A0809206811822AB037A4F90ED9</stRef:documentID>
            <stRef:originalDocumentID>uuid:5D20892493BFDB11914A8590D31508C8</stRef:originalDocumentID>
            <stRef:renditionClass>proof:pdf</stRef:renditionClass>
         </xmpMM:DerivedFrom>
         <xmpMM:History>
            <rdf:Seq>
               <rdf:li rdf:parseType="Resource">
                  <stEvt:action>saved</stEvt:action>
                  <stEvt:instanceID>xmp.iid:0180117407206811822AB037A4F90ED9</stEvt:instanceID>
                  <stEvt:when>2012-08-26T15:46:21+10:00</stEvt:when>
                  <stEvt:softwareAgent>Adobe Illustrator CS6 (Macintosh)</stEvt:softwareAgent>
                  <stEvt:changed>/</stEvt:changed>
               </rdf:li>
               <rdf:li rdf:parseType="Resource">
                  <stEvt:action>saved</stEvt:action>
                  <stEvt:instanceID>xmp.iid:3f5bc854-b7f3-4530-a63b-4e8e84ed7795</stEvt:instanceID>
                  <stEvt:when>2015-07-20T12:06:36-05:00</stEvt:when>
                  <stEvt:softwareAgent>Adobe Illustrator CC 2015 (Macintosh)</stEvt:softwareAgent>
                  <stEvt:changed>/</stEvt:changed>
               </rdf:li>
            </rdf:Seq>
         </xmpMM:History>
         <xmpMM:Manifest>
            <rdf:Seq>
               <rdf:li rdf:parseType="Resource">
                  <stMfs:linkForm>EmbedByReference</stMfs:linkForm>
                  <stMfs:reference rdf:parseType="Resource">
                     <stRef:filePath>/Users/jim/Desktop/Beam.png</stRef:filePath>
                     <stRef:documentID>0</stRef:documentID>
                     <stRef:instanceID>0</stRef:instanceID>
                  </stMfs:reference>
               </rdf:li>
               <rdf:li rdf:parseType="Resource">
                  <stMfs:linkForm>EmbedByReference</stMfs:linkForm>
                  <stMfs:reference rdf:parseType="Resource">
                     <stRef:filePath>/Users/jim/Desktop/Clojure Beam.png</stRef:filePath>
                     <stRef:documentID>0</stRef:documentID>
                     <stRef:instanceID>0</stRef:instanceID>
                  </stMfs:reference>
               </rdf:li>
               <rdf:li rdf:parseType="Resource">
                  <stMfs:linkForm>EmbedByReference</stMfs:linkForm>
                  <stMfs:reference rdf:parseType="Resource">
                     <stRef:filePath>/Users/jim/Desktop/Clojure-Logo.png</stRef:filePath>
                     <stRef:documentID>0</stRef:documentID>
                     <stRef:instanceID>0</stRef:instanceID>
                  </stMfs:reference>
               </rdf:li>
            </rdf:Seq>
         </xmpMM:Manifest>
         <xmpMM:Ingredients>
            <rdf:Bag>
               <rdf:li rdf:parseType="Resource">
                  <stRef:filePath>/Users/jim/Desktop/Beam.png</stRef:filePath>
                  <stRef:documentID>0</stRef:documentID>
                  <stRef:instanceID>0</stRef:instanceID>
               </rdf:li>
               <rdf:li rdf:parseType="Resource">
                  <stRef:filePath>/Users/jim/Desktop/Clojure Beam.png</stRef:filePath>
                  <stRef:documentID>0</stRef:documentID>
                  <stRef:instanceID>0</stRef:instanceID>
               </rdf:li>
               <rdf:li rdf:parseType="Resource">
                  <stRef:filePath>/Users/jim/Desktop/Clojure-Logo.png</stRef:filePath>
                  <stRef:documentID>0</stRef:documentID>
                  <stRef:instanceID>0</stRef:instanceID>
               </rdf:li>
            </rdf:Bag>
         </xmpMM:Ingredients>
         <illustrator:Type>Document</illustrator:Type>
         <illustrator:StartupProfile>Print</illustrator:StartupProfile>
         <xmpTPg:HasVisibleOverprint>False</xmpTPg:HasVisibleOverprint>
         <xmpTPg:HasVisibleTransparency>True</xmpTPg:HasVisibleTransparency>
         <xmpTPg:NPages>1</xmpTPg:NPages>
         <xmpTPg:MaxPageSize rdf:parseType="Resource">
            <stDim:w>296.999935</stDim:w>
            <stDim:h>210.001661</stDim:h>
            <stDim:unit>Millimeters</stDim:unit>
         </xmpTPg:MaxPageSize>
         <xmpTPg:PlateNames>
            <rdf:Seq>
               <rdf:li>Cyan</rdf:li>
               <rdf:li>Magenta</rdf:li>
               <rdf:li>Yellow</rdf:li>
               <rdf:li>Black</rdf:li>
            </rdf:Seq>
         </xmpTPg:PlateNames>
         <xmpTPg:SwatchGroups>
            <rdf:Seq>
               <rdf:li rdf:parseType="Resource">
                  <xmpG:groupName>Default Swatch Group</xmpG:groupName>
                  <xmpG:groupType>0</xmpG:groupType>
                  <xmpG:Colorants>
                     <rdf:Seq>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>White</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>255</xmpG:red>
                           <xmpG:green>255</xmpG:green>
                           <xmpG:blue>255</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>Black</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>29</xmpG:red>
                           <xmpG:green>29</xmpG:green>
                           <xmpG:blue>27</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>CMYK Red</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>226</xmpG:red>
                           <xmpG:green>6</xmpG:green>
                           <xmpG:blue>19</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>CMYK Yellow</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>255</xmpG:red>
                           <xmpG:green>236</xmpG:green>
                           <xmpG:blue>0</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>CMYK Green</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>0</xmpG:red>
                           <xmpG:green>149</xmpG:green>
                           <xmpG:blue>64</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>CMYK Cyan</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>0</xmpG:red>
                           <xmpG:green>158</xmpG:green>
                           <xmpG:blue>226</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>CMYK Blue</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>49</xmpG:red>
                           <xmpG:green>39</xmpG:green>
                           <xmpG:blue>130</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>CMYK Magenta</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>229</xmpG:red>
                           <xmpG:green>0</xmpG:green>
                           <xmpG:blue>126</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=15 M=100 Y=90 K=10</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>189</xmpG:red>
                           <xmpG:green>22</xmpG:green>
                           <xmpG:blue>34</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=90 Y=85 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>229</xmpG:red>
                           <xmpG:green>51</xmpG:green>
                           <xmpG:blue>42</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=80 Y=95 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>232</xmpG:red>
                           <xmpG:green>78</xmpG:green>
                           <xmpG:blue>27</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=50 Y=100 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>242</xmpG:red>
                           <xmpG:green>145</xmpG:green>
                           <xmpG:blue>0</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=35 Y=85 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>248</xmpG:red>
                           <xmpG:green>177</xmpG:green>
                           <xmpG:blue>51</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=5 M=0 Y=90 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>252</xmpG:red>
                           <xmpG:green>234</xmpG:green>
                           <xmpG:blue>13</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=20 M=0 Y=100 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>221</xmpG:red>
                           <xmpG:green>219</xmpG:green>
                           <xmpG:blue>0</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=50 M=0 Y=100 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>147</xmpG:red>
                           <xmpG:green>192</xmpG:green>
                           <xmpG:blue>31</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=75 M=0 Y=100 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>57</xmpG:red>
                           <xmpG:green>169</xmpG:green>
                           <xmpG:blue>53</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=85 M=10 Y=100 K=10</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>0</xmpG:red>
                           <xmpG:green>141</xmpG:green>
                           <xmpG:blue>54</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=90 M=30 Y=95 K=30</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>0</xmpG:red>
                           <xmpG:green>102</xmpG:green>
                           <xmpG:blue>51</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=75 M=0 Y=75 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>45</xmpG:red>
                           <xmpG:green>171</xmpG:green>
                           <xmpG:blue>102</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=80 M=10 Y=45 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>0</xmpG:red>
                           <xmpG:green>160</xmpG:green>
                           <xmpG:blue>153</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=70 M=15 Y=0 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>53</xmpG:red>
                           <xmpG:green>168</xmpG:green>
                           <xmpG:blue>224</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=85 M=50 Y=0 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>29</xmpG:red>
                           <xmpG:green>112</xmpG:green>
                           <xmpG:blue>183</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=100 M=95 Y=5 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>45</xmpG:red>
                           <xmpG:green>46</xmpG:green>
                           <xmpG:blue>130</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=100 M=100 Y=25 K=25</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>41</xmpG:red>
                           <xmpG:green>35</xmpG:green>
                           <xmpG:blue>92</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=75 M=100 Y=0 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>102</xmpG:red>
                           <xmpG:green>36</xmpG:green>
                           <xmpG:blue>130</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=50 M=100 Y=0 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>148</xmpG:red>
                           <xmpG:green>27</xmpG:green>
                           <xmpG:blue>128</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=35 M=100 Y=35 K=10</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>162</xmpG:red>
                           <xmpG:green>25</xmpG:green>
                           <xmpG:blue>91</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=10 M=100 Y=50 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>214</xmpG:red>
                           <xmpG:green>11</xmpG:green>
                           <xmpG:blue>81</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=95 Y=20 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>230</xmpG:red>
                           <xmpG:green>27</xmpG:green>
                           <xmpG:blue>114</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=25 M=25 Y=40 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>202</xmpG:red>
                           <xmpG:green>186</xmpG:green>
                           <xmpG:blue>159</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=40 M=45 Y=50 K=5</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>163</xmpG:red>
                           <xmpG:green>137</xmpG:green>
                           <xmpG:blue>122</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=50 M=50 Y=60 K=25</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>122</xmpG:red>
                           <xmpG:green>106</xmpG:green>
                           <xmpG:blue>88</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=55 M=60 Y=65 K=40</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>99</xmpG:red>
                           <xmpG:green>78</xmpG:green>
                           <xmpG:blue>66</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=25 M=40 Y=65 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>201</xmpG:red>
                           <xmpG:green>157</xmpG:green>
                           <xmpG:blue>102</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=30 M=50 Y=75 K=10</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>177</xmpG:red>
                           <xmpG:green>127</xmpG:green>
                           <xmpG:blue>73</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=35 M=60 Y=80 K=25</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>146</xmpG:red>
                           <xmpG:green>95</xmpG:green>
                           <xmpG:blue>54</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=40 M=65 Y=90 K=35</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>126</xmpG:red>
                           <xmpG:green>78</xmpG:green>
                           <xmpG:blue>36</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=40 M=70 Y=100 K=50</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>104</xmpG:red>
                           <xmpG:green>59</xmpG:green>
                           <xmpG:blue>17</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=50 M=70 Y=80 K=70</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>66</xmpG:red>
                           <xmpG:green>41</xmpG:green>
                           <xmpG:blue>24</xmpG:blue>
                        </rdf:li>
                     </rdf:Seq>
                  </xmpG:Colorants>
               </rdf:li>
               <rdf:li rdf:parseType="Resource">
                  <xmpG:groupName>Grays</xmpG:groupName>
                  <xmpG:groupType>1</xmpG:groupType>
                  <xmpG:Colorants>
                     <rdf:Seq>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=0 Y=0 K=100</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>29</xmpG:red>
                           <xmpG:green>29</xmpG:green>
                           <xmpG:blue>27</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=0 Y=0 K=90</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>60</xmpG:red>
                           <xmpG:green>60</xmpG:green>
                           <xmpG:blue>59</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=0 Y=0 K=80</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>87</xmpG:red>
                           <xmpG:green>87</xmpG:green>
                           <xmpG:blue>86</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=0 Y=0 K=70</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>111</xmpG:red>
                           <xmpG:green>111</xmpG:green>
                           <xmpG:blue>110</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=0 Y=0 K=60</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>134</xmpG:red>
                           <xmpG:green>134</xmpG:green>
                           <xmpG:blue>134</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=0 Y=0 K=50</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>156</xmpG:red>
                           <xmpG:green>155</xmpG:green>
                           <xmpG:blue>155</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=0 Y=0 K=40</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>177</xmpG:red>
                           <xmpG:green>177</xmpG:green>
                           <xmpG:blue>177</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=0 Y=0 K=30</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>198</xmpG:red>
                           <xmpG:green>198</xmpG:green>
                           <xmpG:blue>197</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=0 Y=0 K=20</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>217</xmpG:red>
                           <xmpG:green>217</xmpG:green>
                           <xmpG:blue>217</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=0 Y=0 K=10</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>236</xmpG:red>
                           <xmpG:green>236</xmpG:green>
                           <xmpG:blue>236</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=0 Y=0 K=5</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>245</xmpG:red>
                           <xmpG:green>245</xmpG:green>
                           <xmpG:blue>245</xmpG:blue>
                        </rdf:li>
                     </rdf:Seq>
                  </xmpG:Colorants>
               </rdf:li>
               <rdf:li rdf:parseType="Resource">
                  <xmpG:groupName>Brights</xmpG:groupName>
                  <xmpG:groupType>1</xmpG:groupType>
                  <xmpG:Colorants>
                     <rdf:Seq>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=100 Y=100 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>226</xmpG:red>
                           <xmpG:green>6</xmpG:green>
                           <xmpG:blue>19</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=75 Y=100 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>233</xmpG:red>
                           <xmpG:green>90</xmpG:green>
                           <xmpG:blue>12</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=0 M=10 Y=95 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>255</xmpG:red>
                           <xmpG:green>221</xmpG:green>
                           <xmpG:blue>0</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=85 M=10 Y=100 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>0</xmpG:red>
                           <xmpG:green>151</xmpG:green>
                           <xmpG:blue>58</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=100 M=90 Y=0 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>40</xmpG:red>
                           <xmpG:green>52</xmpG:green>
                           <xmpG:blue>138</xmpG:blue>
                        </rdf:li>
                        <rdf:li rdf:parseType="Resource">
                           <xmpG:swatchName>C=60 M=90 Y=0 K=0</xmpG:swatchName>
                           <xmpG:mode>RGB</xmpG:mode>
                           <xmpG:type>PROCESS</xmpG:type>
                           <xmpG:red>129</xmpG:red>
                           <xmpG:green>53</xmpG:green>
                           <xmpG:blue>138</xmpG:blue>
                        </rdf:li>
                     </rdf:Seq>
                  </xmpG:Colorants>
               </rdf:li>
            </rdf:Seq>
         </xmpTPg:SwatchGroups>
         <pdf:Producer>Adobe PDF library 10.01</pdf:Producer>
      </rdf:Description>
   </rdf:RDF>
</x:xmpmeta>
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                           
<?xpacket end="w"?>
endstream
endobj
3 0 obj
<</Count 1/Kids[7 0 R]/Type/Pages>>
endobj
7 0 obj
<</ArtBox[78.7725 155.908 623.663 444.145]/BleedBox[0.0 0.0 841.89 595.28]/Contents 133 0 R/CropBox[0.0 0.0 841.89 595.28]/Group 134 0 R/LastModified(D:20150720142046-05'00')/MediaBox[0.0 0.0 841.89 595.28]/Parent 3 0 R/PieceInfo<</Illustrator 135 0 R>>/Resources<</ColorSpace<</CS0 136 0 R>>/ExtGState<</GS0 137 0 R/GS1 138 0 R>>/ProcSet[/PDF/ImageC]/Properties<</MC0 131 0 R>>/Shading<</Sh0 139 0 R>>/XObject<</Im0 140 0 R/Im1 141 0 R>>>>/Thumb 142 0 R/TrimBox[0.0 0.0 841.89 595.28]/Type/Page>>
endobj
133 0 obj
<</Filter/FlateDecode/Length 957>>stream
H|VɎ1W*;+Bq{2a_9K"C~:}zLJHo>)ʉ.5ҟo3#xnUF_JPլĵhOSGmYmւTnĒ%H 89X}n/v$z9:<?E&ZMRᠵVw[OPZ&my	qp4THFq7$Ki͵0<Ny_+"j~O4TeoSX4+b'fkru_y#mNйM8b{>Pέ'%ugJ@r@/3ZME/))5WySoTx_
FME]0~U4l}9&G-HHa>`b*hah<ZP
c+LuI*ɲ8Y1.K?&ܩvNFx
@IWMa)E.8V{{3/1b藂)e`ANPĒ=|:$Hٕ6A8S"#Pݵ|5Գ\{S. F9:Hn>zsN5CCwdM.2kNUwiݫ)!b~)>Ez6f^T7NH4m;
7QI#X(pWe:DÖ2&}T$ [q[|Qdk02
ZEQEDkz"ϱ'p%F=1<!Ī.3<dϤjjin
;`$G
n
hCD-j׊Ss؜	pV(hj _UrOQ;xaB:sJد5{8!Ǹ_=oH/?Wz}Wj
endstream
endobj
134 0 obj
<</CS 143 0 R/I false/K false/S/Transparency>>
endobj
142 0 obj
<</BitsPerComponent 8/ColorSpace 144 0 R/Filter[/ASCII85Decode/FlateDecode]/Height 74/Length 868/Width 105>>stream
8;Z]!9krEJ'Z]AHr]#]?fMWl8:D(FY#cU>X/k[,i#/&&Mfsk0%$7bELP`;2e69sDW
oNeBdV:!?r'ROA(@@f31,;[+4o\Nq2!3TS;EokH/X+-f!=?X>[(KjmAeY,1ha!@3/
`np??:[ZM8%dA9EH=EDbkDYP3eZQ(@4NEAB2t<g,DKR\1LW,6Z\C'.e\fEH%TDV\s
KfL;JUHLE5PgT5`N-[0`PVd\;MK8Steb-254^(U/Irtt:=Smj.O<0SMd6f@0PlJl>
Hoi7=C"+[Q"sHk4;c7C1^@E=NA7W1eY:f),S".'1bDc50'1T&nrY\1G`IJ)"kM79j
OX%En%fi@#dreP2q]'a*+[;o#)4(QKdE*B/:PP'AA<S82?fMF8,q2/rq@gdUP!G5t
F!R'gJqHl,&5kh^.fS-IW\q*(OT`W_i]iV9^m\04Q6;HjY0j(+P![BKrKa5k]6>E(
e$-qoUL]o?Yn6NC.;Y/A\k/9OHi`_!Bom1ljIM2_#%0W/U:WhSjCs]mNjKBbap=s0
&t9,t-.Bpb7u`Xu\S/m?etPh&l.7euaH9RQ_Qa(q-R_9(8W6qo@UtTGe4^kfnYt8k
k&X&hNGNYMM&aVI*Tj@pD2u1s2E;CG"p!MRU5ENK!-F7rfBi7,Af>,R!ltk.cUfZV
H;=SZ@R!c>+IrMT>s=PjR*Vd\Zk'PdM(WdGH-@)RmPX$-B>5OF/n*O^$!JRcEui\(
pK:Yt#+0YP0?,>PJ9)aZ:1(<.Wq3b[R0c*11X*C)OlH8AftKN0l$,3eq\*_*>=4qH
oDp]t[d:H\`Pe7:E/(BIKIUjrqPZUh%JorFs15!VE;ED__7W-@&*(ft.p&lY\b^g@
cN*$l'-.~>
endstream
endobj
144 0 obj
[/Indexed/DeviceRGB 255 145 0 R]
endobj
145 0 obj
<</Filter[/ASCII85Decode/FlateDecode]/Length 428>>stream
8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0
b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup`
E1r!/,*0[*9.aFIR2&b-C#s<Xl5FH@[<=!#6V)uDBXnIr.F>oRZ7Dl%MLY\.?d>Mn
6%Q2oYfNRF$$+ON<+]RUJmC0I<jlL.oXisZ;SYU[/7#<&37rclQKqeJe#,UF7Rgb1
VNWFKf>nDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j<etJICj7e7nPMb=O6S7UOH<
PO7r\I.Hu&e0d&E<.')fERr/l+*W,)q^D*ai5<uuLX.7g/>$XKrcYp0n+Xl_nU*O(
l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~>
endstream
endobj
140 0 obj
<</BitsPerComponent 8/ColorSpace 136 0 R/DecodeParms<</BitsPerComponent 4/Colors 3/Columns 500>>/Filter/FlateDecode/Height 500/Intent/RelativeColorimetric/Length 23780/Name/X/SMask 146 0 R/Subtype/Image/Type/XObject/Width 500>>stream
HSTgv5ZŴMLkXD3	ZQ3^(HenS#sO~*B@/!z	!,Z]A!=V}+BY@%	!N}aBS@sA_b7ЫFN! :#k*q!TY"&BsA"٠G1<B*	zQHu#!@Ov	%f+N+!d#A
:ŠWt	!t	q"N:!NuDЩ'ΠM
t!Md犓?R{S$N]|~&٩vr[7/o[=wt
ȑ#FgWCBj8u}[W@@"WImtL{>s;ti5ʢl[azcw_թ‘n!V]hx0\?lKX?<ßҾHN{tK辚HAd̉c.&wGg)M+yэ!Aw4=·67)S>8"8/p!Ab5Gf[6;zWfϡk'?ϦEaG7j
`LS458W~6-禆)aѭ"G15S!m{Sɴf<[`@7o4LQ[/OԸZrsoї "t6TF3^<ϝcQ*>m<sdؐd7_6?k##cќ6]>BL]b,F ^mnR;FWƭVyt	$>d'Z[LɛQ,aG7
dk&5s-&to<ЙZt/	8BI/?Hֵjǣ;JȺAf%Bհ?T͗BXU@5.ʲ44*-oseA@cYbۗaP&|yt`B]%_1ZUֵ{Fg@t)=&
6,AIm-ЪuKxR+úNRuÞgo]
頖ЈʩNb&Ȩ#3HJC-&N@mh@w8t0	̏:&yt߀n<?茿!5S?LIĶZt_:Ľ]DZC5
 68u^v@?
XV\.򽤫qd[.ry݆Г@,:eQ.{u+^Sjh<0?];y-+u^x)]g/~pF6;
+L
~OLkئwX	lVK8'-kp^֠7XlDT[=R.koS}G/jT?#/)w|,z?H
~<BҊx72qbPQ'HirNT71x
)5+!$ *s*xDn/}&e?YTB`@&D$I
51$idakJxUHKC"?`sIx(zzDxIȏVvYK5D1ljwFJx.&=
x.K$z)HNQ2lDiZV~d%CHݡ]J-ɛQ!5"CfltQv_Rɉ&uћD*db)Zm̶2oܦF|G(}O2;[[	ؗRz%$Wi(_6~xħuiFoYRwy]fֺ$'"kBZ9#PjW=H3z*HKƒ3Sj{/%^dYe<:BҪ0rN/h0O;%]8z[9:9ԁֵ{՜.=#s\A|pO8usKir^5"i3jq~<۔:\W*ms4rN׺x)eVNPw5L)=KY9	g.t)Kz_)	#PEAFg饔vB5R_܊K9	w4<5y#*ags$\ixV)˰zl[v)mWC&6D8Zڎ@o0^wdRJѮޘ[B[a~gRZOE	fiħH)7Sfz-Er.t)iD
!謩RoS23Wu+QJsث:#荴$^$-x(NMRZSoQŮ6/<uR	ֺYhꤠ2z!ǝfǩKKwOGf}X4gӚajJ+T#^>xd䉞Xgw$J/,O/kSF*KTj,\mJ
8aQ	ۇeU"Bs{;EOP`[SqEFV|Jw|XcpQQ
K#ڧ:KKNttۘ֔pŧlwV>vyH,;lK'FGp9YX6	@w";3b=ms'7%-baɜ;[۠\-ΨloB^ܝq{6P~|%<taK46RRfl I=U!F
j1jsna{4+u'T:e6
y2;bsZnoMAV1jCs:Nmm=C|?sG&O
-̅
SzmX$bU0/lfpEGnܺѲgof`3?2Jݶѱ*Uv=֨pyz
s{{U>,4hxL^:2U;RkJ}N4hVL:v鵎,~jswVx/є]{Iw\[jI7Z5zv슮oovx]Ňr cStV`ϩ>Ӕ:$)sZZ;+;K~n;^x	ug4~ii|Ƈ^^lbTR'
M10ڣ-6ѯΫZ%>prt~3ъo#K
`
zRJ۾&ʬKh#lϛ:1	9wm6أCtljmY?е
ŀ?>1S@5v6we}|O:-tTF_0~,OC@%g_\w*ussnHO3<:^G7P]VjHrrҗdN!t6 &R0dO	ZWjn󒲤'CM@|l+X0:
5MJ/hR%Nvyj
bI%?VH-tT:+E>)fdMMs6j%|ٺu9:ۻՍe$VjtZ,n'}a*p+O~xCN
MlQ.#I$r%f4BVFOq
ftdb)GlP" 5SqQ&19K$Ʒ)bT"#%Zl2lfqh@`Gm5+VEkV0PVhe}9So
PY!ÖaeVDA,j>̦|UN&	D
{ `0& {]y*t+fM.5[!:m]Q
cI^WPۄi0LsD1IZW4Kr{='MyzޑQ}"\/$ cZZ1:ut+fMD= q`t
Szڸul]WrK?ytϐ	s #$wWeV̎\KN|lOKI̵vaxi7z@4zOmOE
a&et\~ݍY{=Nazg@T#BœQ&
}2:{H[2l^D$NȂFހB^`zvF*%HE:Ȃy(
)霽mh
Sb팬d-,1RX?DF+]Y#_֡R1:?D-%8@^']Y%wW
)|wIG}6fFb	5AV'p1rz]2lIJ7ó~ћmm}YF,qr˚[Xp:X_[J݃Ac˫;^Sg2Ea짆e"7=R$1o/(U{RDw[!jI(f~mP7ܛ+?~f@vC|Sʿ
{SغX3jn7 *Ecd_L}8vhWjU);ߟEJg=[V=ɞ;͉?y4_9a5/+7͹I牟C'W]RLvǶ戾L&/8Z^懎?GۨOdcf7|ߦ?WD1I>c{ftUJ.D7A5f D &r]柦tk.]]Zj:edՏV$Onm)Xq{򍈾!'ޭ4[رX2]=$;7mop~2Fr z].	?~:@1yA5)|抂"9zd”V*lK4<Yg_Ѝb.`M	_$$q\+dEΔJWEo‚ν֞W]r::I;NU;mjtL@Ɖ6ȤFdE%RDe)k,v]=oݔfX.y9gs+_i+<"6(= Uc~){%Ǜ3EVjװcU~!.#}iDצ*8\`~)%,V.ֿnup?VmTPKzD}0xi/Bh_+z}ܝB*T݊NGO}5^N:
[?^S*uLd~#N98IMmķۜxuO}	j&W
]|+#JrE'K9V;Y*79'xiP0xe;7Hpbb7zYP8\|qB-K928%^o]nty=-7>s %hL$%M$W;G$"*/H^	gsS.O9=W&.ƪ'jsH"@i|W֖8G-Ķ $"_3Oqy_aiD.t'p7ΐMLb뮗Şм\~.+g
dȬ b	3䙳iDw'άلdrZ<:֥K?Ǿ{}
!B8r/3y'G2YGKKHzǩՈ=Iu@3S4.LStrNy+BaiZw붠ui!]e?z(㭤]/OB^
Ƿ(fD&=&KUf8$/3ݜٻ'nhX0=J"q	_SHF(4SnwZwzbڛ5u=	ipƦs%S29UAX΂_U-'9g"De;iA2H
]Űjx`1lbywbO
u3=m2(<7Zֳ}c+VtC7-抬*%fB+&M bOBd陓ͦW5Цc؇gM"[#vQ=̂
V7.0gd뙞F"|g1BX
{܏.R>تht	t>AJnS;`wd}m4Qb8trȿ=	fAn[v-{ﱻB2UTa{$&.7}i<.l5=r{r{791fre-A>Nyc9UNB70#4U7P*w
-~vס·h1%tƧצaKNI؏7g~FՙT=Iu;>F/^C$+,Ny#KN=Ky+̚M\	YUXInJF"D.Wybf4ٱƈS|-9{>o#Q:rQu^cف"$SFcO8j(5=:gM/q{\ڏ_vmm0cQ]PX1>ؓZ{R5PD4QB?l85.HK}9eʀIu$Va;.v
}8x6wͦWurxCLb]=mD7*?
WۉLJ˔W8n*"XSe(tO1\B7*?9ezbt2%<zݥ>.u73KRB:g%q]0n|#Z̚MeJ6Vtb|I Q!
^oEw/SZw^;>i
^
db,
;T9a)(B>v_I˚1G'xuU05/R\k&0\Ftr¨)ح.|oxYs7E{t{=_bR!^cR/Jg`Ui?+D/kj7Ftu?;i!o>܃1W9A UG5˚|2\lݍUb7@wΒGzT5Z2>iЃ.UNx{#F/kr|17luD{L9!E`K7Ql%\D){,Vb'2ԗ.^1\ooJrx\ףi!|G*'"dݺ
pWD:ob׊Ʋ# `wc
`uυYѽY+n붠W#採z}4;C!Ϻdt1<&qx4t`<]"}UC
o~j21#l!ȥmnLt|4/FW+:3˧iUj,󆠏NhL-_Qħ]!{NVt*Gm8^Ot9oP7l-LH;cǯZՊN/`bo6ufCZ-ȠbL
Fa'#ՊΧ k:t*Cvݖ`XD
At-CCѭ7hr4H/튎"JSSg?Ngӝθ^:UXٖeRU.EZ("@#4Hȅ	IN٠;t<yNW>y;-]]1UN`
7Ap|èf𯪆%:,pRU~w\a9BSV3ъt[eO_EmT,/EWKH[rָT={c}MS^Rbx7Hr+UL
Fӽu+私{Ctr5NN-ޡSFBMf~WB 4XnDJ`ѽogd	^<Uiߞ&$j~yFl1teE>ۻNt֛ioF/>}aNrGrִܓn36IuJ*NϜ=|5StOK:>8ܓk{]+2zb/tG3l<ݑ}QnrKIKފ왧WFW@d>a;{ȧ۟M=3G@VXZΤPEr:bݲc>Z^rAXCo8&vȪG/{+;΃6ZLȧ{e:
ځcRldV"ͨ$9U1>\Mc#+m#q(iǙi~SRl7-tӱ1MD:
(p[$j	\n,1(Zb]toE8U\u6Ԁ|wmrZB]=Kp6x؂tl7F]b3˩w/=X.(4XVeؔm:THVd[/әtn\
f{Ȩɞ5N\i{ P"m.F۶Йe5A-h
FQ<э%&˰/JDVp!zcaXbIr+1X01ii+ԒlOJ[ޤc+O[jzw!XnȒٗQE;C-TӍe56C(~g[]v^,TjLו[s?jQG]RӍeU(tl7nGgZӪ<1Ȟs>=ܧqN@ͨyȲ2jtWՐ{Q=RWH:v`F|q~oGL9騪r;q)YPce
:7YV)	(߶gd-:ż0g(xzʮYQjJzSn,+o 1߶
ti݁u.za@VJYC-U. $XVs3bm8]Zy)\ZGUUͮ@m0=Xf־gRm3bmOJOIVp'D{閪cmM
Ce@nfqљt	y~lҊf5`tKVus4}`tvp9{yy
:>dCm2aw6۷-UaȾUzZYN 7LRvj
'ؾҊ[Ug?8vgr_Xo{,ܯegRJJ?y}NgV|3C+ڜ]Tj1K+tf)"iԆy_ҥ߹Qz	Umm_pZRZj>ךtf)MQvYh]ZՌ6ڮ~̬>r*iSWn0Hg޴Y0?V:!FKt?U9ܜs
C5Z΄},*"fp:YJdh/OW:,_ډ%:qÎY0?ҙަפit<RWP{ʹ_˳+]Z[Q*cK+:LSesZo{*`Ij@߶?p7ҊR~TsȹP]TjD
Wjљ˲LgRȀz3vZRZj)@i\3Ki5R%9EgR2#nzs"|d[btf)W2Ѷǒ!:z >ݧ^*9nUjQ̳Oҙ
 *hۗn:+dnuBb2
ۑ=؞Ltf)Y	Ȁ
Yy))Bw\/s
yEDTم={>JV|QԀX}tiwXjJv%g|nOf*iZkbCjK+[!$Rs3T$j^.׹Ҋv3v&]ZA<t3K(d&6lGcu.<ж6_K+C<q"'Q3Kӥ1m4K+"zc:"㓨,Ktiw,f>eҊqHΦwYc*t5Sr)s.>-UE랫ISٓL+|Q	c-FV|{/@Sжy.z:!u˩݅~j
|QYO.>`m/x.}NTy2O4_EmTӇ}q9EV|lW^ZCV|0dzt<\eՕRc||FcIJ"q\TL=1* (ʦC
EEYzivriF6o=_9RO+*.ηѴॅ@boa۾]xicثu|吏qFGH<rdE=݌2lv^}3r*/s>ˀM4m
<Oq$*>'TҾ0*<3i3AHZs	'W_~B@Uu叜inkzxE	KTvø4	xi#=vF	?">LJqi;	)vIIeJI.e<e@8NOf43̐;o]޸2ҧt'sogAi쩤m{xi#D\$J9
W@vsˀoc#]<љ]mK˟^T.o53>tjfھ8Z?Ib&xNDl̨ATdq%</4i%dxf!(dilZWK˟)8ZƙʊTj	s,2}9BӦp,v!l7?:.xQuB1K48v%|IJO2}I~_Hg69ص^Z<$£ZG9qHy;SށdRA&|Y$9g`^8<z ߴҗpjh<\8H,G2Mig?9ION=O2U=6{BoqFGXv!Ux$ӔA'$='{K˟$
^@tkK8YJQمd:9Bxi#LbOn{QWf{nxZtzNk>{s[o\ݵp|/-v[iQQxiSr=GdV,MCA]D'e%/:ÙkadS9/-TiQq^ZNbϗp9mz+_>m4pq#5wL;nFϦ
/-iQ/-uDb/.uRd^5xx×l9¸qk+&	/-J~nK˟-XlYo,\GWx,&F|-ӋȢ[f?n4g8ZNxc!15&5ST_;50na_4\(|q60K˟~*mzॅ
ZI͞g=o6cPk.0nVL#zi.JJ~|i3bTCBq3˟bڸ_/_srcFdE-n73]hDv\YvODJvIN3RG"?>ܱןg̊JݣfCfF1,_P1HvEUP7G"ЗpK˙:QxWi9ǂ?2.PLMۀY͇J֯?g&'+R=xoy2*$	Uo<=^.u㳙#첟LU,<dӷ}gϷTc-7mdEGhPG[d\Kf$<YpJS	~'
IبSO.76m$E<gxꜫwھ=/-v䙩)h{B'IF7@+Tz?>:o]ltf!d1\	agBD/Wm͖ۖj*<:6ˮL#BhqLM=BͣrXlYooU^]Q[>.+$1.EQԒv|?L4ٴ
:1xf!Jk/q>nOUuw2suU*:4R<%
3Bxl	S9LEbYZVۛRj;5	IBi>޵}/׀>{lBpyTSvEQE18tE
gf9=B'HJ}5uÑ	xfoL~Sh<$U>k^quf$E,\[x}56
9ՠ(<:c1tRn
,GǨ6G85dcZ5?SH-U.MwBjry>
,]9TG7N	
,Z[4?ܶd5EdY[ʡw[3aƥ(Ð8SLKBܦى+j`|ob}5߈ϩp
^ͦFxfѡqQ"}KCu;,
J㥩MEH!LꇢfE&ʒۛg¿k4NNEQ?~J^cBM+Ӛm$!y;\M‹.]Br튢|vxf!Kٓ,6zP`z/!GHsU0)g?!Lb*۞mk{cEDQi`d
L[YJ ycjX2W'6+"Q`?{l4|h<(:4	Y&SzVq$DV8C
䘧Bhr0/0}&xcQT<8Hћói8)|2vGic5\5,E2O'mwmƢJ*ɩoǙ,'ߴ}aB|]iQ$_OMgjvJwΎ;VM/kT.
R.DXV\ȍ\='99ۣFs'[{&<|ScF7-{J`
+v^L;[H䨿	]r(7n~9
aGT}_L&\R`M˞T(v)Ew,zVyO(^	;RyǡV/E,"vTtx6Y>%^>/g;Olw@Wd &n>Vb0wauc߱(\;ڥ޶=-Sa
tbWI7n1T'swF|`9[zO-@UH㼸ݴZ8zxFH|-T:^tQ)bD2lVbAo#ZՋoZƔ#U!
k=GM`
c|]jRxߔǾ2~kPn4R&Lo`@'?vtӲga*kRݱ#V&bܸ]׹sQB$,|܄%ǡ[-K!(whM˘=guVr]ECW
Xf<sg$s߅SE'l.<!euÕ0s]|`+`~V߄.\v^ܷQRdHߺl0{j{3
Vt2b'Cw?F,.z$TRѽO?4J,.[7! ]hqvfn'k݀.XDjO@)ś0]oDWT?LQJFmw@~p:ݫ4'`gvݴVY& K\kv)'v`"t{͈^ּ
b,w߽rh'~vހ.[ƜovnE,.}&>]q]>Űm % GK;
P-"e"խNң}JiYZ`f;N'
<le̥_N.nԻHvEgOt/ӽܸKdn't3G<ab-lY!KLKb=ݏ/@+.Uc_)E7W3୔6سnc{4˖1`;LtWˁcnJÂ}ts^$$_2NO0JU6ᇬ(!sesјYAq;jC+:3?V*exy/MT͌'/o~çoƀt'~t2gaFrw;	%A+.w!.D*‹JOQf(	QVç'	qB2˖%+M"cfP._J[=E<F7,FvdkifFfCe"q1aFHnrVt7$	*-bW6E,%x[l	.+'KfIu/bWUft߲^
];nOqtC6Vx&]F7l'z.g4k˖%J
q^.qUz?ބVt{KyTvW\Hy7"=BVytNNԾEG"͜pupޢ@Wk>0pU)Iיb)~dO€% Gk$ncRA-Kz=:vWԊ|@ybcQ|h0T_=.i: uuݷ8^iAw;uZFbSy.e/zKዟ,@jͻ^cNz_<SMݷ{v>e}n{#^toE./r{p:	23E{
+7	QF'u;j7@=VaBW.i&#
fɺvoݫy=}`2C3
^6m?Ԡ
)Ԯs e$K;G̈́Еˌx̒<q;!baۯѽ|P|	TFM48
5vyS$Dj(swr-|#z|zݫyBD`!w*ЕLdW=$B~~/rp
fO.<u%6hwls|56i
j8B$ ޠݺnޱo0{d턒m{5Onە,D7)*@+75m03$Pe,ó1mVdeU"unW+K
]}zS7)zKipɠCdZՋL|Ќ̙me0X8%
\;wKPnᘺ .D]IB7C
,F7jʍE~FE7yHRT$x	]Jvb>_"vXJ.a;zW"Wf(m!;%&%S6W{+\!ф"kvū4ۋ4ó!E{]RjƿE7jPغm8j+.)eVtpkf&.Ъ^|Kug@]>~){E؇7
a65(6GlNgƠf$_+7	=]s6E#[ݫ(O&E!buCcܟ:8Glwӝ;[nnvSio (/ *D!2PĠ!'9!BH!䞜=n;
b.ϛl>}O޴nTYP
sLDw禷go\_!&]qdMFhݽTSxhi՗rv{
NemuYؒ3k+cݠ"4||.f"6]7
?a#~eI^Vx]rˁK~!>}i_9=Jye1$ G.q_z]z|qz%a.Qڿ]rˍ
ؒ_Sg7O4}S/2'eT!B];_z9u	K}GwRɛۮ)BV/4;<^zYb8@ⴾvOt)t^Z%	
]=|~t-9[E!5A}uۉDͮ	!I&~Q{'{t	'iꯁ$vXKt/TQK`{~I)xɾv;н`\#K}bO.Q9.a)%RE*ܛ[ː6k\'rӟХ`
qY$hi؟0l	q`lbk)Az?Ի]2̎oت_+kEtc'̀㿎PT^̓y_o?Ơ6
6i.UA<SA(mtD`v]&CuRl_R/tw*LqIoKn;!"gT_FbVl!vQ=׈d#:Ko/nE1T67\vG+Ke]⾭ݢfR
RL{WК7f]Q;Bwu25"d:śӲzYyRߧvx] )iŨW;ۉ(lk}ݢL&o[R\C5aY]|&G	SH,x+0`x^NLԷE]Wdzo1T[Tu3>#lKY.Z.])EN!qx+`k89't-olzYdLn	–UmtYza;_JT:EhL[].*ocebqq(uHLƂnSFA)82vpWK*3j$I?D?ϟ;cЅh\$v(;xކ.|zM((O|+Lq5ARY?}~N+gv1,n?I~yɄ-CmEWt
٫ho	tS4re=uZ's1̓VZ1 M~r.ehf<L]d :dz/)f`kK#<.4sQ9}#lW'3-1sEns=ty<-g{x]͹Ȍ777K{|Ynn㖐|?z7s$[[aAb`w!0يU{gD{Bָ]ty?E.3D2HaIqKp)tb}:#$KA;
[}ts*"ZՁO&ЏeǮ.S3B0l
~_n%sຝ޿ӮE7"#.}үEw2`|؍½Beʰw`~<o8<.J7X<ʾhBצR"-#^aZZK/"#`߈}o-vmeXeZj媿{R-R~<?-zfg]\sK=3
?`{Wg#6"M޽[G@)/asѺ5N"ؽ*?Gsѝjzk@68ncq5GVmnftg*(-n3uNV]W]m,>'Dru!Yb{N'Zvsd._KRq&Gو[´RaZO`C`[|y-md1?]fIbwC`[Ozc]J̞5~qOjZs`לC`{%U;~~"i"]Sk`7Cy`
;Т{R)/J:lB/԰is(	lsSZIrH<H\RC qKAi_%L/jv;j.;b6wBO=F7#?4I	wJ:t]+}r1]LcwC`;;Qh^vݐήw:hM8.,cBTLyf&v9|H<XC7s?@B<g+.Zu֢O_n.RurhՠFwcC{J	>6ྷN-6T{ϳPlICUݘ[S(Eq%84]+f]Gjv};cC\tbw#t*71kKόgj=Sq;S-\GqTl0ւV]KV%a	l>=̱SE}s?{7L!$ͮv{qݫ‘4^yq?f瘷PJR[ei==ű?|{z@";VT;>.͓
_;"bv~ފJo__B, 9nE1f>Tϳ}b_A? Cmz*T1ľ <*r2TY1x
v+ᘆJkP4td|3#C	ЈV~^
6AY%_bDd!bD>gf)z*TӐ1	bi`7qTh
zE+67Zu,ȀHv4,S"zEiV푘솔QW@
)^{[EMM$9#Pw2E?Bδg?
بÂx7e_\s@;p<VJ9V\cV~r׸u0/T[Gk-hnGtw'w;NOr?ޣt[QGkI߶wsL	UoNB~wE4:AzLQg⛬_#1el^ԋߏ>ra@ßHs)'gl?3/58[%%ݻެϱȻ iRƜ@+V$!\b#ſL7nW[:YuZK
)QKl1ݸBaUo$?tClgZ!mf4OVt]ǿzR_ߌڗWsںp&|bwhȹtת44nnw4%i/Y;:Ɲ`-8S鮖ey[gH)[o]pok%O&'CJ<˔d,ָ|3BUQsYzP1>{^
/&3}4蔜^{ɪ\߸XZݲhw%\)x5/.Ou0
,nPɢ#5G+:mܝ|??<;-~yF~#ڒK{펌O]hHɬyfۑ,Ĩ=@WLFw_9{_nJ5hF$g5\e~xavɹmV%_li54dd\kkH~T@6}m{@}<t.7v'z~uG5ۮS	|a%cU[c5KчF~*6 Jv@HLJ,Y"r/9:ƍ_M&\Ýu}wn:muL_!N
#Mhft}e_~Эt͋wJO_ܣ.;G03W㮡{Ѫ^uFSTy=9mvֆ'Qşd#D"6fk
Se($},gtW[sE$?T%ݠa
fE$*61&vwͦ7g$I&*|p݂ݚn/^4$~h`l݂kSi$IF,쾔GOIvׁ	vSЍ֠$IPk55ݑrt)#$	dI5H<$roCv;we$ݰ7{Q!@f{:zIg{r[3I"0Ʊ :T✯߇L$'1TrzeƝ$IrF._dzQKN2
R1ƲѳJ<=d;ct3`7_DDNNĒ$^3q*97۾G-IsxִW@"`~DO/IobCnCZ/a$敖41
s֘'$ɟTw]s
шYqUg$6BP[]KуMkt
bՈ"fv-x%zI2UiYg\:v{s(hW9617d[zNv7$0❈	c,%
=$x5v{AL8e=y'I{#S9c1/D}:
HRaF:an"aJ>GTWNp]n'k_I**6$
R~$)ktȳn)b	֦Ҭ@?KE"2->GJo/$eޒ^GDi])cRV},̨X@ؕ<PG$Y]mCSRעI	U!'@ ĂF	;"ɰ)!{p'cz"ItcTA!!>Z;eA,D4bӬ)qf:(Q"eNɲ8c*p7]1sq!$s	ʠ /AAnK2@mϽ	?9VU$+01NjJeY}KC?3Ō,>5Yr붻7` 4x)pI^CvÃ\'z0Qm~wu=Ά$x
F(IkQ!;͇CQ۳}dUriWtڄ^(A*OmUdiJ3,z?HӀ)O2L)~D`}A:OGp^UYa-;'֠7{)as2D2MIV۔‡X9$79~KX0?JAlGA:=](KO0u[WtaBgR3wGtfWg9\,)3as.tf_?
^v 9PȲ>Y
>9K'5oËqLZBX}szZ#Y>6oC%􁿄n<ȲlȮ&rzǕYb}!o-n;|H[rH[r2./48ݼ>S+rB7HsS=mMsz׺t۰QW]QFSIXs34B1"еX:nO
!srˣA9šLt?^i	aboڳ|-tkV]H3ڐgI
dWo\}[
DA7f“+_èԊK]9#-Q<YNi#F4(З<t;Vݡ%hcQ>:1$G?EъI&tVJ]u{,c[xE7j€."Э
w?kx`̂ץXfg	DхnX؄obь7fؤV$kMv|sDn[$dY~|~9>uA+g\D?H{Fݪ%{kGu(ߏ;Z_~Ϋn*.eJOdWoNnJ<ߞ2tWj[E.F){-)x,>W9jM""t/U&ҸqBǑ[[
OfՆmYF$!:tGEzownqQӾtS"|l<ˎ-C71~tcX|#C?NiPIpFuތC2:tK
XSP|NҞƽYU*Uw}yiaJO. )Eq&Ov)Zc|-j nگ$ݔ[C-',;~ɚ[vXbuZSq#%^ŗnQAwZg/RP(_Q$K/~Do2"dK'2"t)8&9<I }C/-?%0}D9~TC Ez`(GD1.}zHMk""AE};D衢B_
zh!EQ\AOѡ??9'0&"Ө?'FD6pj`HD^y皈ҿx
endstream
endobj
141 0 obj
<</BitsPerComponent 8/ColorSpace 136 0 R/DecodeParms<</BitsPerComponent 4/Colors 3/Columns 500>>/Filter/FlateDecode/Height 500/Intent/RelativeColorimetric/Length 62605/Name/X/SMask 147 0 R/Subtype/Image/Type/XObject/Width 500>>stream
HSTgv5ZŴMLkXD3	ZQ3^(HenS#sO~*B@/!z	!,Z]A!=V}+BY@%	!N}aBS@sA_b7ЫFN! :#k*q!TY"&BsA"٠G1<B*	zQHu#!@Ov	%f+N+!d#A
:ŠWt	!t	q"N:!NuDЩ'ΠM
t!Md犓?R{S$N]|~&٩vr[7/o[=wt
ȑ#FgWCBj8u}[W@@"WImtL{>s;ti5ʢl[azcw_թ‘n!V]hx0\?lKX?<ßҾHN{tK辚HAd̉c.&wGg)M+yэ!Aw4=·67)S>8"8/p!Ab5Gf[6;zWfϡk'?ϦEaG7j
`LS458W~6-禆)aѭ"G15S!m{Sɴf<[`@7o4LQ[/OԸZrsoї "t6TF3^<ϝcQ*>m<sdؐd7_6?k##cќ6]>BL]b,F ^mnR;FWƭVyt	$>d'Z[LɛQ,aG7
dk&5s-&to<ЙZt/	8BI/?Hֵjǣ;JȺAf%Bհ?T͗BXU@5.ʲ44*-oseA@cYbۗaP&|yt`B]%_1ZUֵ{Fg@t)=&
6,AIm-ЪuKxR+úNRuÞgo]
頖ЈʩNb&Ȩ#3HJC-&N@mh@w8t0	̏:&yt߀n<?茿!5S?LIĶZt_:Ľ]DZC5
 68u^v@?
XV\.򽤫qd[.ry݆Г@,:eQ.{u+^Sjh<0?];y-+u^x)]g/~pF6;
+L
~OLkئwX	lVK8'-kp^֠7XlDT[=R.koS}G/jT?#/)w|,z?H
~<BҊx72qbPQ'HirNT71x
)5+!$ *s*xDn/}&e?YTB`@&D$I
51$idakJxUHKC"?`sIx(zzDxIȏVvYK5D1ljwFJx.&=
x.K$z)HNQ2lDiZV~d%CHݡ]J-ɛQ!5"CfltQv_Rɉ&uћD*db)Zm̶2oܦF|G(}O2;[[	ؗRz%$Wi(_6~xħuiFoYRwy]fֺ$'"kBZ9#PjW=H3z*HKƒ3Sj{/%^dYe<:BҪ0rN/h0O;%]8z[9:9ԁֵ{՜.=#s\A|pO8usKir^5"i3jq~<۔:\W*ms4rN׺x)eVNPw5L)=KY9	g.t)Kz_)	#PEAFg饔vB5R_܊K9	w4<5y#*ags$\ixV)˰zl[v)mWC&6D8Zڎ@o0^wdRJѮޘ[B[a~gRZOE	fiħH)7Sfz-Er.t)iD
!謩RoS23Wu+QJsث:#荴$^$-x(NMRZSoQŮ6/<uR	ֺYhꤠ2z!ǝfǩKKwOGf}X4gӚajJ+T#^>xd䉞Xgw$J/(4-cbtbdc䌉
*Ko{ݚqF#!@}uu? t_Uu2M@łmMi:vLqN{pEmĸ8qn
[W3f===sĶ+=9U}ji,D
<U<18^@/'ٞesJvgNJ0/]KJI~c&F7uMXXr'~6h׹M(*W^gIg2ϳ(Ɨ+[\\?Z|*U:H,
5x]ҨC__,,Qcp
ۣ9\iת37ʆnۢ2Lϓ<W3sr&'.KjZ}c9ZpUYr*sTgq/I\ͮ8ͺ<!zl6]z`.?Yk"+Qn}a4+ܰeWsw 5Nܾ:uZ;+mp'y~`wMo,4W<rvj!=^;zp@[ʦ!~R>h;ҶKutg[P_gb{	%vjNW>wc-i
/KzsmС7mK+.˟Zz;K'=o}poY+6̥qQio3."2GЊWl{uLz).ί#csr~R'ŵCȢ10]6^RU?7'>;zTη%GBFz]zP?ì2+^";"VXVG6ݓ[ztșz-mY[|ZYIb@7K_^_;	'5I>BpmN}<a4TRo;r}]wH+9>SC7fhخ*@<F}zxџfN!ʕ.YüNYAks
ݶw%;9Rx)c7{X*sp`b|yiVw$G*hWҨK&^K$@	-ތ{VZBi^)w]OƊ]pzc`-	/6<VB]⁺{b/ŵtNN\V;)4D]FޓJEט᳔fQb;!&}j᳑0M,>@E1E"EzNV)<M,21K,wOzU)ibTQxC.ہy},0sr#2Ex@L
jT]a`0v[Umb^n>0=4p4}ORNaa@`0&"GEJt+MuZ!&mQ4/'l²NQ5ҷHi>FF2DlG>׻ccj;n2$oY[G䘖Uxi݊y#1&lO.aI'PwmF+~菥?sY!jAA$G2Mqʰg؞7W(H5vajkA4y豟ڞn+f&cִ\qݍy.az@4(a ^&m2;I{l>8щH8ĂFހ"1^`z쌼'VaxK Gפl/9{
%!%wvv@<;#/	E=G,VNYq
5u-!%C!S3XR3bJ
c%!.]q߄nHkZBğ_~a_й?Ak.]h-Öt3}(
?oH\U;Є{XhXh5mzEe6B.;"x:aٲNH"1ST{RBO{!JH"H߯j{VU7VMo~Wڽ0kף0Sƌ_L(YP$v`Njk5誔b!!I%,F_(f3?<Y]zvūwrnuSn֣R	sݳi1sx:޺ye{?Cw%v7!6GÏY~4_z`FWQY`+dDP([.M}TN}%C=wHh-V^.g4Onm)X<IgmCO}pδcwn̝NcmFݱyOx	-_O[$_ <לӭK`{vg"п/X#W-Li@ψhguإ+D صC<%=
dVA{ߙRəz#6`	FEvs"cvͅu@oO0g%*}~?7u]qL&ӤNS	I$dbdXHXppqlVoXZ,ɖdK,Kzڥ}}TlΕG;{GF3Z"eP2jSȽ̓, h}c؞,SE2J,uE?L^7A3%MO9Vɜ+?^,~Rrk
qTUK+{')Ѕvu_G鵦3&0"3b)<aYQS螌6gY|u'4"$-&Z@fb	m>HGRVDhܜs<xT_/]j*
0G5=>xGQ1_?.yizk˓$l$]5vz`IK9;_4&lAg+%=I|>)fQ`lkryrx{|dt\ %iPZ89.u~rR2r1EJ'sjq2S\"'/=;?F,ׂܭAiX󏯊=޾}dAG!W(lܮAW3ja#ʙ[?hYv!viLaC{n-}zy)w9ƙΚ `'W(S2lZvr,Ezײ
LzKʩG=GshTO@P)?9		pFPnk]-\3
.Mz&F[viq18?',x1˓v2_,㻪-'j3`63C%-&j'jӀf1}?Oۮ-.B9n/}B'Qj?mC?ArrK(
m;3H|mv8I1ڛ3>rŞCiQ\ؼBnβ@gdNܳP7y-'1VfWwiA2ޚ$joXE%=c>>:m.>7g0`}%g"哧ltW&ɄY]o"6D9ru/n]+NE!lըHX?]/U>ףV7iٷlNFf^zgӌa
~`F )D"lx΁dnM	so(g5!XzW8eWF$dn񒋔CO;sDV8=oMڬ^oL୐K0=.=Ya;Gr%$Z=Ҏ_:U=w{Qc\cMbu'ZESޘa{%
`IcG3	rr[zvG|o5,;MzӳJRl恝lmgl>U=G`fiKy+Wbһ$ouX2vg+.*R\y`XmŞk_J"ŘKrLZfI+NIdXVh7݈MCΗ2;HĞ8jj,˕{XNb)K	V>:ٿvD0W'{`NսޓHuQEWWȍfWa@NUhD9{~ .fӆRK7]E}8n՘\{\d8ʡ8'F凓7;Ua
8
01Ry}ȭND=EIQE2E}QEivz8RAoZ۬&1
֑%W06U3ld`9IgOrÌ"y
ʨ
d~aQ9Enױ͛7V.K|a2aa-?GTGEꂙEc_O{WV1MoEgdcQN5zȥ	[$	T3tϴ: w&VrC5;?O ܫ0ev['2}T؜:r{oN$!˚LW6?T9_uݖ Iz(`%Ww>5jlսT-r%'*'H2d(r<2r'שN8%vXb
wz,ktF~2JX\,ƄR~ wH:[S䛞Οڅd$l	jFz,wut\$}w{,h0>Onrmr]Z=xѤ7idGL *'HdYp7汣	V&V.U;!wu;b
5ݸpEIU _Ypͳܷ@,!uqt@
?>ahf^}/`ʼnpM"3DXrDVmYrE6
[cJ]7&~4_,N`-B_$rqsINԌ*WwtF>_*WD␏BEM{0,gK/o\:Zz܍[^&^KNr4j"Fa
;7tâE\qr-pS@
(FŸ5Z=g*BVrYk.Fr?İ(e€Ljt'2h`9jK
5,<QbJ"_D(I1'Rrm[fBkkOVR.Ӌqkm~UJ͓4~tܞRXK˱Y
"5[qk7`h)6rәvnawǺlGתe]bud,"ł\
@B.BBn$$93uXQ93<?3|/7ZVJw"K_;O̞m%"*zܴ4В3ҫ(&ս^W.]y/}ʆ寥zۈ.ZOOq>=ʬ_Ea|+[	$K,!|E׽i3To	hw<Qc۟V@W3$
C ~2\ƭ1ϝST\|T?k=^ڳ`JOHQ"r`s'9n
ΩIz|eZ!Ϝ[}$
CyuOKǥNr|+滥Z8^$Ҟe&
fZ(\ݑ|Qow咒`˝	{ܒj=@{_=p{HQaBۑD^]$\ls|+Yhi9M+ë
-I	_x8Z;=D@oE|MQu:E'Zzr53I(tX"m@o/E[ʩ޽DU''P$%U!=ZE[ڎUQF֥ge,K"󳍮jST@{ӑ.zl5@(ȫ:Dۣz+;Xd-U@{
cjΞ@Ҏ88H\Ppޱ=D`*ߜw+4kU=l9<٘egC
ʡ/{(3Nᨱt,*0H]À:RjU\
44{(OdvAo,W,@-ݙ(Eېe1G|4`Q%(团z8yYˮa3p;=DIJbP1iX1i~cYe7E	KJZFU'	%UL,HdfTHd|U6zcYL.P%_nѦeTg'Qbrz];|v=u8jOo,G+y\}]vu썮5Ӫ܉I%J`t.8,NoGiT5i'jӼEk(wG]힋^kP$w*2zf)0bۣ|xvʵO+w'
SzTuBXmP02PgEJ%ԓIWg03	ޥGU?dl@λUzs%hX@q{POnk>=uu6:Ӌn**!ZHTu*
.XƉFb^i/\ې_Ћ+ׇ nM
'9͏wQ=~b$I)}W;ջ%p>Oo޸>q;c(ylY
0?;
;;X%\RQnMsw~.ٵRe+ʡ϶W>gAG+ȟRmOoC=V%:g¾ӳ/K1V]sG!}zE2
&=ywDzTZ9xn^Z
I	gkPlɪ+w"CCnqs7U1IXN陥Qv	̾{%ĝJCyzBuK
%C=([ӄzʬrWo.A9'T\:).H*)Э}8H,`~
[Xun~"0^SL[s<{(܏esӶuzf)Զ{1Uʝ=4S489-|Dzh\g?o02gr'ɘ_VZ"Ӱdw[STjA	T#8˩2_U&=X/ࠗJQ?_ST-^VrU=D
_3KAalPOSX!#i=K̘V{(U1F,N.))ncYA-]xCxlnl>F,1n{,&>f*I.DstRR*q"^LBۑ4>=V虥0H@/ge-)m@6-s
⼪Da=OK=3QlPO
rJ`m?G|}Da(kU$r,;1C}m,ezB쥕$=&!3v$Zs.W]jϝnF[RVjb/rӛ[pE#1RK/T61m3qZYήCSf
n(v̡4՞3^Cf+nf)7@Yb/N(CCi7՞W1m0ZYZf C-l&[g$
CC	9D^ZKN^b,fޥF-z9ҞY2(m1ziK{*gfnn߷r&W1D5U!L	zi!Tt۹em7`=:Ǖ޾9~Š~vZ^Z9|vEz7kr}Aߥ=3g@WQ1mKRBs5hktSϸϯWak혶oھQzz?uKs>/4\}i{23KA{6gW	=%FUzu"ھQ_1Qy7&llI3}4[JFֵ5c=Z[QD"(2(r
0030=	e]}?ߌ7ϋ|O0;KvBw_Z	7}3Y/^P{ѥn8CAֻ*AWϣeza|;hƘAw*љXt߀Z0~6h;QBe6ME:# ۾z*m$u{`cGoӂwKR.S-1zxb3d{/2+@K؉n_5Ȱ!ncm0*d9q]\8Un!9DJ9>+049:(4ԏZ8tÆN(u~4.|!J`l:&$h;Yz@|.4oީ䰡Vc$Xt!/!2F٠9]ZHtM k'->a@x^^%QyPAr
|$+`lҍ3̢d	}6iW6:Ф5Fa@QnꝵϹF[v#ͮ]u3Y8jw2*CjCE#	hĦ9&
E^&̢#oE<'YB̢&zs]EHkzk -E^d9ҫVv^iBKB~ .(8^YN+z+u\~hRW<H"k|%Tu/tgqHf>h32@Si([a:kg#O꘰]*OjX$ourO)9ƭA۹cdS9.-6]34Ldr~/zF+nGop%zQ pu
,t<f(
G	/ʀlpu_Rѥ忉90A^*e%g[Asz,",簛zr	\])3-+^8";?
VO#].X0Y	X8,jfM%dv";D+~ 9a
:]$Jtio^f>hզ>\c8AV@7d:<f1(V-T	XuZ^D؂eFpJCִ|I:TbѢ5+@$3ƱLn
o>MnSN3a2L9P9'lE3Sc=/-@dm,bcdC6:7Y'ʹGilv2J2D͒y}{P-%&(-o]3Dg*d@v5+eztiozLi?([&{6-Vu:[8`Ռ;1'Gw(}&#ۗ}tfQj0`zqJNW"ȀF.-MQĹ\*LxBAڒ@ٳ(XNخQ5κPƢ͈'<Ģ(U틨ـ,J?>,|r0zR$|Xm+Sivt&RlY}P\:jӢ޺gI_b|6FҾdFЙE09Q5=^toykrPHS|^(}?>ҲphEhvTH_U=GiK`.K,|uj&uEŨRw\*1d.3liIzRځP,>^>_n8>QlN6O?0&K%954A}S"Fe(1Or@!6g(
ʑ'`j}
v"ZUlYwb
%	O?o}/9=̢̂S`B+nUE 
Rm"gx42l|+m:@[)-OS'p/lW"ZtMwңPM"
xy#ҫ3%0N=,]qӕa|:ݻ=,GoƢ m9G(P4ܲ	ۇUyj~CR];2M)ӟkg7El@gec0NzIa8]8ͦڸ6-)Q(`}B𒎹W{]ȭ 
>koWpiRC̢Snp*MPtXjƱNk8c|_jDw棔eKf{<qלu}\~DNR?|'KZAxM+-mxYjǜPNK^Vu jt~{;e5;heMlD4	r*K>Xp͌;1!is4+ꟕםnYnqKPϾUi֔e=e>eGǷ/h)zߣgN*SvϺ޼hV&=:$uǞL(u,~؋B)uŕS{|j6_vkwR[85!Q.+FW%k0nl<df8(*ȍ\03"s0;n<*yswQ?<2bgK,8g]B;1tś6,~|;³Sݳ}YTF@&OK17Nn}@̭q5)
cv8_)<$:~4[-ufW#p+3OPmh}mǰЊ=+סڰS{7³Ra71?pQPt"П\qMӶˇH佧/z_/Մ`P:>5l2WY,Qmh5۫yXhu0F$튋b=`$vo2zB>TPj@80|
WS17sFSCإ[NjUŁ0w/Q`SLӉM,]cEyP=O>N.Ռck#^\0HM*C
C:	xglNzH@+dDH	?Ԓ1D,(ؙғh.zެcU'IQ7fQkgCGv)̣ XD*àzl}ans,-Ӷ+pPXh%>yofE+*Eu
]8+[5ؙҊf5
i8:INlF5㈩;[K`j$1)WRO"zNt`*0UK.qO-70B=Ёt2nGIkI,]s[C"q6m_,43$1MFҳ˴w0URw<RTAm3	LN(pH-r-<]pۭk."nBr4-?_'R2nAĒTvi~>ߒy>\|}T$Mf2VPgAvroRUXQ?YTa9ˠt4,Y&zRدQJ?ŚZV-iw|̩3%6]BbEuA5<e{m=S$HRK*CU4l=NT`&!3V]鿲8{y(P*Br:Ui^f&ZYPJgA6d^%,TA5-C`Vɿrdu~5דr#27@??>iL9f߳Wĭ5|^巶$7"	"}w$GBblZ-'/.-MD+6J1EJ.Rm%Uqv)pè?IL4䞤b	z7BͿn/Oy`LsͤdP6i{MGFo,]ג2))l,Ɩ#av.-hVp*/CW)D5BNݭ|}j
}He0Tu}
KT2G"n]{vwB☛K
C	>!FJΐ܊X)8Z̲kdPB;1lrTIZpɢ؉H֑o%%Ps(RK*AjwA!@c.0r̵Kl6_j:VhKׂv1ׂ(1`[ӹPX,ͪBvievioZ0
2+MN-^jACvk"Ñ7׵J)ܝT0-PuG|`L78:j탤^^Iuns2lo"w{Cu:]&F5dpgЀlħyJ^M5ՒVx$;?i1ڑVR,ܓT,!UPjBnH3 ƫtsÃdЭ+Q׃./mV$_G~yL(Br6coKrSJ$M;wW+PB
fQghTKҬn=e#l.zQ R7B6҉@2106N<]$?{oA|WCw!!ۉ#NeൻjG-.ɰK~U7ʫQbE?<m1RDع=?s&SLG—TBjOn/}
0dS>)9Nд1ϵ3aJ@?儿[IyNv~o٬\j<4.,wӱ^#3lħyH_t^;:gɗmT:۾p{
?RBf	u}Ӏ!
8Lsb1ߵc}U}xci@a_eevSL**sa.\$UQi6e]AQQct4#h0.;ʨ;" ݍ{ߐ	2`tS%w!8\KiԶ1[%&ɣC9㦵}Kv_m$nuƢ]ז,6$vc\<
CZK5z=0rϢ$4+<)!:)PuYk,7EXzcoϺ9Sf
pt>":DֵMcR`F7㦵qVk2_R"5@NRe)OYn*R,LQbJHыl5oDHUGfB]>K_1nt.	5>>I[8+w/ .#T42o]$.EǸlmӗp4?k$R&
9%U^t3qu;CjK456Fc;orċC$[1H)Ԝ,_gg.VceKzZkABw>.܍{j>RԚ
w#'U*ŌY'֓,1qUωfPE+OsKfᎆچisO.ėG2$[䛐b'R}ՉPsjd[ǺMX~W~K~cFGŀ	G:变wU^Ih7M19KьGGvpΉN(`tt?%\RZӔۻaDgQJ($_["Cup6
YWnynk[s܎ޚ_TqŐ?gE|]8v?5
lR}q8QqV9M6w{\IO3bwR#Ip;C]cW2lgbJAI)zW"H5RP5\̓a#num?>pKQ黪dnyӳçn+~
xSsO>%.#\}^Hl7a\6Ω$0N#7d|nUsN
fR}^t?)<Y@<CgDI(i0#j$)!:'7bƛ.diw-glڲ9AܰON~	x{@Z"_J]F?ӽh}m֖Y߆e%b:bdʹi!MTS,_I|1b])/%
EH7"vUԤA+aYmC})Ѭo0J0;o SX?*MI&'ɔW)f\6G3~~j֜4*q qN L\b^nlgaD(?%
C2$[_"UG:jҡ7r{}Sa3w=}#
\/!##"Yd.'.#զBmL6Oi
;-j_g."8t@c=gNrM	qN>aR򂒁i\%$s)i$l=oFHUG{9?Rn1]~T8s#' B1w!ܭNlGSȼ	>᣽͸lmhtDjd0'A5J4gDWHNB~tl'N؉xQ`D/DUH)BXCɀ,][)b7v:5{Rῴ8b_çn_oHKȶG֙w2(4$1M[ّ֖""q,"sm0'ei3n|C]wAJI|)i#j$FH-TD9Pw7fIX^~~+J.ϛ۴ u9~xW
z-_a4-kH̋mVq2Qq>-."8a
kr1d|NUi(g3bO}#LLQb7JOI F؆T'A9͂ne
6vq7kv{YX~o*V
ræG%v}RW`:U¼-Lj0Hӷ3[[&Ow""8Z?*'5O	jF%	Bu{aD>1)'%KI#"yRlG=Pu&j.V-Bĺ;|ז	7,w%dYo1;&laD$R3.[[fS@g"\O?\_AMy;ۙ3awv#"yURAEZZ/X^.bUG"X(xArQFHdet3</}t䮜K{_aIIGɛ2wBKȃ"\Tkz";?CfBF_ۛqvY<^Iq笻17,?3'sq#g%^<$!c!1[qj5wA⹿=
O%8D/5$cdVEO5t=k;+HPD:H$"dE(Br1R@/:iv"PwOBox]#n\6_:mֆb fM}9n8?g=rh&{rügo~y=NL.%TQ/^-u g d^ܟP"e$RD
HB}Pwꓡ{vӯn'ڇ~RשJM%WYl̰?	ԥb xewcEE=:00L;[bS~/>3>a,@Y(31q]=/k+
t:t'|HN2&!G(ZkPwSA0F_)ta16yuTÊ)*{9wR#'p> 8iyϺm?NJn{tϾ_J
Z}k9ܣ7?? '7̯OnS`)HH9\dܗH)RF/
u.OAfCUFM
n1v6&lNρo+[^5/,ՀArcʰ\dEQ
~$_(ˎd1ʵO440/8Xc6E&5Nz
[}I)5nb?%4S~Lk/I"BIY")#rREHiPNCCv{و[.v{U[ȓWs2ϯg\{p^3wl?
]D!~˴>HCr햜[aAQ8}I^.;B$&g3$Ýt&d^ܟP"Zz"m<Dh8
"4WFQ[Q,A{es\4;iAGF	 3,wYQj]oZ
&3%v'G?ÿh!MR*cq
كqA쉭lKcyV!t#IJ2&!G( 忐j%RGى{'Or{9he}nW<C#nv~V#}wkκs1/·Q./w$ ]r%oR~ghW}-Moh!M$'X85P5`%~i҉v{ދpo:NI, 侄<P#RZfBڽPwO@C[ݹͥŭGFwbs9\
b岝2>.|'u	mǀ.Fӭ$/X@I+7n];dO/}el3e8TqW+L;
T5U. g%g~D2<HyrFEHTKiC}PwOBf@%65wNC2vQk5^'0YJ?){9]X紅r瀻0{EX{;|I&[Y}8!
lu핦%U'vAGЯ44ER`яt!;Bȼ	`#b\TkzlG8uaF(][]˞zd?J~{YQ``^kύ	@pgw)hl9[L_ύZ<_O%O鶮2ba,
اC݈ҔhcE='l\%%#t"FȘ|Hz3@hLhv{9T9VkwJ[.OZ]<rkNU2am+ZJпgOG(Y}@<v)׍VXh7U2R/,)b;YY.`DY(\u0
3e܌]I~5
$ 1K:y"侄<P:<LGL;L}-r)Ƅxh5<G!HCHQ@XoAq,uyf3.@^ԱHBA; Ƭvh[:ZvOP=mU%å!9äX
RJ
1 J3+=,5Ǧ=>)W*kJk6,ZsWC|/!ۊ-bG+^hqʃEdm/||T=Suc](`{/u#(|E0BBZkflCڽPwꓠ41v϶}5.Mn:5>qKG{q2#>G:F G(/IGIjӗ?8r[dʎOm6X^UMd9olի X=CÃ/<?[,/m?4ԓŢ>`:cGF(ebg#v1R-CHi#>;'!Vۥ܁+V^T{ϥMPX㳽oՕ9y?I~˛
x1vab	o%tnn*ۙGFEmn/⻶Fɿw셟羮:IJv^W.tb0r0r/F(g"v.b r_!7H@Ih8
hhkzf{wն߸Rxgu?)
_x)!{_"?[l_k?89:Zk*m{}Z7ln/YqH|Y|v+H'[=Od<MjqY/V)&UT{oޘ#we>bG<W"zي;64h́o7\L4x$zw]Y⢠PYKH8.UQ :Y@RJj߱6\Z~)vpvKo߃(]o!nMW~>l"xѹfrYS>
}v=0t܍Q2`FHԫz|OCC\e\S5Tl~:u8յ9u͸q^{Ɩ,)
!djdD|,oo]@xq:Hbic1)E
`$B7qgՃ7l#Uii9EȣS)?K`x`)}v30gw	;1rF(CEH FCPuG?t܁vdžvf[oxTsyF4='B'vab䁴Ԏn[Mgq;
e*KoNinܡ!ن]l3=mwsom{*`-<OAȽE 9]TːKل4ېvPИv۝˚d7uwUe%#޹aD?>@/ $ EvٻLڣ%w3س̓b\`{EJZ{P`CRɉIC/|dxOHnwl.a䮌‡Q`ም%H"fَ{~xWeUlop-s<%Mτiq+J兢?E}()@9 v-򶦜ˍCmɝ6GƬ=*k2:;d{UINΣcIڞ۳`SMI30ۻce23Zf@}Pwꓠ>V܁vwdN-SRyg[ߪ+
/:fkzG u!@ N|V_V)\lGrӅ-]94^q0!۳PONͣ~1Ҿod{ZJ@n9bgF(eRF!vR *^4[v'#PA3amoyvlӡZ˔n:~_,,

u8E:~,|	@t
@Үڬ$2x3т=.2"[#L|Vjx
UvV2jۉn_*>=Nؓd!!0'O\W#b2ш]TKz5R"Hj.OBC,헹\u(;dvNȴ3ʹ35</KH4XJh$(QwQ!r* BPͻrv]K_}6/ϩ۵ÝҴ:
CݯsNQk3cp
,灠
5O41Z[C|a’_<蓽6:g\\mN9d-]xF;"OR/*%{ߡDz/mI#$)%]F03|VĊ}Xy(RBsH|4tb;>nwni#u{><8JoW#`~$7xA1o%o!;Œ1.%	{qea09ϞԾ˾ws!`0Xi>@pi
L6ٛDӋ|h;9юĝR@J3k1l'XXu!u
Ҝ7.ll{L?lpwjq)ލ;7K P
Download .txt
gitextract_k9q7365g/

├── .gitattributes
├── .github/
│   ├── scripts/
│   │   ├── build.sh
│   │   └── build_guide.sh
│   └── workflows/
│       └── uberjar.yml
├── .gitignore
├── .htmltest.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── doc/
│   ├── README.md
│   ├── antora.yml
│   ├── ds.yml
│   ├── embedded.yml
│   ├── github-actions.yml
│   ├── modules/
│   │   └── ROOT/
│   │       ├── assets/
│   │       │   └── source/
│   │       │       ├── Afterglow logo.ai
│   │       │       ├── Blade.psd
│   │       │       ├── Clojure Beam.psd
│   │       │       ├── ColorPalette.psd
│   │       │       ├── ColorPalette2.psd
│   │       │       ├── F3.psd
│   │       │       ├── Launchpad Mini.psd
│   │       │       ├── Novation.psd
│   │       │       ├── OLA Logo.ai
│   │       │       ├── Push-2-Stopped.psd
│   │       │       ├── Push2NoEffects.psd
│   │       │       ├── PushNoEffects.psd
│   │       │       ├── Show Space.psd
│   │       │       └── WeatherSystem.psd
│   │       ├── nav.adoc
│   │       └── pages/
│   │           ├── README.adoc
│   │           ├── color.adoc
│   │           ├── cues.adoc
│   │           ├── effects.adoc
│   │           ├── fixture_definitions.adoc
│   │           ├── launchpad.adoc
│   │           ├── mapping_sync.adoc
│   │           ├── metronomes.adoc
│   │           ├── oscillators.adoc
│   │           ├── parameters.adoc
│   │           ├── push.adoc
│   │           ├── push2.adoc
│   │           ├── rendering_loop.adoc
│   │           ├── show_space.adoc
│   │           └── videos.adoc
│   └── primes.md
├── logs/
│   └── README.txt
├── package.json
├── project.clj
├── resources/
│   ├── afterglow/
│   │   └── readme.txt
│   ├── docs/
│   │   └── docs.md
│   ├── public/
│   │   ├── css/
│   │   │   ├── bootstrap-cyborg.css
│   │   │   ├── bootstrap-slider.css
│   │   │   ├── bootstrap-switch.css
│   │   │   ├── bootstrap-theme.css
│   │   │   ├── bootstrap.css
│   │   │   ├── jquery.minicolors.css
│   │   │   ├── screen.css
│   │   │   ├── show.css
│   │   │   └── web-repl.css
│   │   ├── epl-v10.html
│   │   ├── font-awesome-4.5.0/
│   │   │   ├── HELP-US-OUT.txt
│   │   │   ├── css/
│   │   │   │   └── font-awesome.css
│   │   │   ├── fonts/
│   │   │   │   └── FontAwesome.otf
│   │   │   ├── less/
│   │   │   │   ├── animated.less
│   │   │   │   ├── bordered-pulled.less
│   │   │   │   ├── core.less
│   │   │   │   ├── fixed-width.less
│   │   │   │   ├── font-awesome.less
│   │   │   │   ├── icons.less
│   │   │   │   ├── larger.less
│   │   │   │   ├── list.less
│   │   │   │   ├── mixins.less
│   │   │   │   ├── path.less
│   │   │   │   ├── rotated-flipped.less
│   │   │   │   ├── stacked.less
│   │   │   │   └── variables.less
│   │   │   └── scss/
│   │   │       ├── _animated.scss
│   │   │       ├── _bordered-pulled.scss
│   │   │       ├── _core.scss
│   │   │       ├── _fixed-width.scss
│   │   │       ├── _icons.scss
│   │   │       ├── _larger.scss
│   │   │       ├── _list.scss
│   │   │       ├── _mixins.scss
│   │   │       ├── _path.scss
│   │   │       ├── _rotated-flipped.scss
│   │   │       ├── _stacked.scss
│   │   │       ├── _variables.scss
│   │   │       └── font-awesome.scss
│   │   ├── fonts/
│   │   │   ├── Lekton/
│   │   │   │   └── SIL Open Font License.txt
│   │   │   ├── Open_Sans_Condensed/
│   │   │   │   └── LICENSE.txt
│   │   │   └── Roboto/
│   │   │       └── LICENSE.txt
│   │   ├── js/
│   │   │   ├── BootstrapMenu.js
│   │   │   ├── bootstrap.js
│   │   │   ├── jquery-2.1.4.js
│   │   │   ├── jquery.console.js
│   │   │   ├── jquery.websocket-0.0.1.js
│   │   │   ├── npm.js
│   │   │   ├── show_updates.js
│   │   │   └── web-repl.js
│   │   └── shaders/
│   │       └── vertex.glsl
│   └── templates/
│       ├── about.html
│       ├── base.html
│       ├── console.html
│       ├── cue_grid.html
│       ├── current-scene-fragment.js
│       ├── current-scene.json
│       ├── error.html
│       ├── fixture-definition.clj.template
│       ├── fragment.glsl
│       ├── home.html
│       ├── link_menu.html
│       ├── show.html
│       ├── sync_menu.html
│       └── visualizer.html
├── src/
│   └── afterglow/
│       ├── beyond.clj
│       ├── carabiner.clj
│       ├── channels.clj
│       ├── controllers/
│       │   ├── ableton_push.clj
│       │   ├── ableton_push_2.clj
│       │   ├── color.clj
│       │   ├── launchpad_mini.clj
│       │   ├── launchpad_mk2.clj
│       │   ├── launchpad_pro.clj
│       │   └── tempo.clj
│       ├── controllers.clj
│       ├── core.clj
│       ├── coremidi4j.clj
│       ├── dj_link.clj
│       ├── effects/
│       │   ├── channel.clj
│       │   ├── color.clj
│       │   ├── cues.clj
│       │   ├── dimmer.clj
│       │   ├── fun.clj
│       │   ├── movement.clj
│       │   ├── oscillators.clj
│       │   ├── params.clj
│       │   └── show_variable.clj
│       ├── effects.clj
│       ├── examples.clj
│       ├── fixtures/
│       │   ├── american_dj.clj
│       │   ├── blizzard.clj
│       │   ├── chauvet.clj
│       │   └── qxf.clj
│       ├── fixtures.clj
│       ├── init.clj
│       ├── midi.clj
│       ├── rhythm.clj
│       ├── show.clj
│       ├── show_context.clj
│       ├── shows/
│       │   ├── chris.clj
│       │   ├── sallie.clj
│       │   └── wedding.clj
│       ├── transform.clj
│       ├── util.clj
│       ├── version.clj
│       └── web/
│           ├── handler.clj
│           ├── layout.clj
│           ├── middleware.clj
│           ├── routes/
│           │   ├── home.clj
│           │   ├── show_control.clj
│           │   ├── visualizer.clj
│           │   └── web_repl.clj
│           └── session.clj
└── test/
    └── afterglow/
        ├── core_test.clj
        ├── effects/
        │   └── color_test.clj
        └── effects_test.clj
Download .txt
SYMBOL INDEX (203 symbols across 6 files)

FILE: resources/public/js/BootstrapMenu.js
  function __webpack_require__ (line 6) | function __webpack_require__(moduleId) {
  function renderMenu (line 113) | function renderMenu(_this) {
  function setupOpenEventListeners (line 192) | function setupOpenEventListeners(_this) {
  function clearOpenEventListeners (line 222) | function clearOpenEventListeners(_this) {
  function setupActionsEventListeners (line 226) | function setupActionsEventListeners(_this) {
  function clearActionsEventListeners (line 254) | function clearActionsEventListeners(_this) {
  function setupCloseEventListeners (line 258) | function setupCloseEventListeners(_this) {
  function clearCloseEventListeners (line 288) | function clearCloseEventListeners(_this) {
  function classNames (line 480) | function classNames () {
  function getOffsets (line 555) | function getOffsets( offsets, width, height ) {
  function parseCss (line 562) | function parseCss( element, property ) {
  function getDimensions (line 566) | function getDimensions( elem ) {
  function noop (line 1051) | function noop() {
  function arrayEach (line 1121) | function arrayEach(array, iteratee) {
  function baseForOwn (line 1173) | function baseForOwn(object, iteratee) {
  function createBaseFor (line 1216) | function createBaseFor(fromRight) {
  function toObject (line 1249) | function toObject(value) {
  function isObject (line 1280) | function isObject(value) {
  function getNative (line 1355) | function getNative(object, key) {
  function isNative (line 1404) | function isNative(value) {
  function isFunction (line 1451) | function isFunction(value) {
  function isObjectLike (line 1472) | function isObjectLike(value) {
  function isArrayLike (line 1493) | function isArrayLike(value) {
  function baseProperty (line 1532) | function baseProperty(key) {
  function isLength (line 1560) | function isLength(value) {
  function shimKeys (line 1591) | function shimKeys(object) {
  function isArguments (line 1646) | function isArguments(value) {
  function isIndex (line 1721) | function isIndex(value, length) {
  function keysIn (line 1768) | function keysIn(object) {
  function createBaseEach (line 1816) | function createBaseEach(eachFunc, fromRight) {
  function createForEach (line 1852) | function createForEach(arrayFunc, eachFunc) {
  function bindCallback (line 1879) | function bindCallback(func, thisArg, argCount) {
  function identity (line 1927) | function identity(value) {
  function includes (line 1985) | function includes(collection, target, fromIndex, guard) {
  function baseIndexOf (line 2019) | function baseIndexOf(array, value, fromIndex) {
  function indexOfNaN (line 2050) | function indexOfNaN(array, fromIndex, fromRight) {
  function isIterateeCall (line 2083) | function isIterateeCall(value, index, object) {
  function isString (line 2134) | function isString(value) {
  function values (line 2173) | function values(object) {
  function baseValues (line 2194) | function baseValues(object, props) {
  function assignWith (line 2281) | function assignWith(object, source, customizer) {
  function baseAssign (line 2318) | function baseAssign(object, source) {
  function baseCopy (line 2340) | function baseCopy(source, props, object) {
  function createAssigner (line 2371) | function createAssigner(assigner) {
  function restParam (line 2435) | function restParam(func, start) {
  function uniqueId (line 2492) | function uniqueId(prefix) {
  function baseToString (line 2512) | function baseToString(value) {

FILE: resources/public/js/bootstrap.js
  function transitionEnd (line 34) | function transitionEnd() {
  function removeElement (line 126) | function removeElement() {
  function Plugin (line 142) | function Plugin(option) {
  function Plugin (line 251) | function Plugin(option) {
  function Plugin (line 470) | function Plugin(option) {
  function getTargetFromTrigger (line 689) | function getTargetFromTrigger($trigger) {
  function Plugin (line 701) | function Plugin(option) {
  function getParent (line 768) | function getParent($this) {
  function clearMenus (line 781) | function clearMenus(e) {
  function Plugin (line 874) | function Plugin(option) {
  function Plugin (line 1200) | function Plugin(option, _relatedTarget) {
  function complete (line 1566) | function complete() {
  function Plugin (line 1736) | function Plugin(option) {
  function Plugin (line 1845) | function Plugin(option) {
  function ScrollSpy (line 1888) | function ScrollSpy(element, options) {
  function Plugin (line 2008) | function Plugin(option) {
  function next (line 2117) | function next() {
  function Plugin (line 2163) | function Plugin(option) {
  function Plugin (line 2320) | function Plugin(option) {

FILE: resources/public/js/jquery-2.1.4.js
  function isArraylike (line 533) | function isArraylike( obj ) {
  function Sizzle (line 750) | function Sizzle( selector, context, results, seed ) {
  function createCache (line 864) | function createCache() {
  function markFunction (line 882) | function markFunction( fn ) {
  function assert (line 891) | function assert( fn ) {
  function addHandle (line 913) | function addHandle( attrs, handler ) {
  function siblingCheck (line 928) | function siblingCheck( a, b ) {
  function createInputPseudo (line 955) | function createInputPseudo( type ) {
  function createButtonPseudo (line 966) | function createButtonPseudo( type ) {
  function createPositionalPseudo (line 977) | function createPositionalPseudo( fn ) {
  function testContext (line 1000) | function testContext( context ) {
  function setFilters (line 2009) | function setFilters() {}
  function toSelector (line 2080) | function toSelector( tokens ) {
  function addCombinator (line 2090) | function addCombinator( matcher, combinator, base ) {
  function elementMatcher (line 2143) | function elementMatcher( matchers ) {
  function multipleContexts (line 2157) | function multipleContexts( selector, contexts, results ) {
  function condense (line 2166) | function condense( unmatched, map, filter, context, xml ) {
  function setMatcher (line 2187) | function setMatcher( preFilter, selector, matcher, postFilter, postFinde...
  function matcherFromTokens (line 2280) | function matcherFromTokens( tokens ) {
  function matcherFromGroupMatchers (line 2338) | function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
  function winnow (line 2634) | function winnow( elements, qualifier, not ) {
  function sibling (line 2958) | function sibling( cur, dir ) {
  function createOptions (line 3036) | function createOptions( options ) {
  function completed (line 3430) | function completed() {
  function Data (line 3535) | function Data() {
  function dataAttr (line 3726) | function dataAttr( elem, key, data ) {
  function returnTrue (line 4066) | function returnTrue() {
  function returnFalse (line 4070) | function returnFalse() {
  function safeActiveElement (line 4074) | function safeActiveElement() {
  function manipulationTarget (line 4946) | function manipulationTarget( elem, content ) {
  function disableScript (line 4956) | function disableScript( elem ) {
  function restoreScript (line 4960) | function restoreScript( elem ) {
  function setGlobalEval (line 4973) | function setGlobalEval( elems, refElements ) {
  function cloneCopyEvent (line 4984) | function cloneCopyEvent( src, dest ) {
  function getAll (line 5018) | function getAll( context, tag ) {
  function fixInput (line 5029) | function fixInput( src, dest ) {
  function actualDisplay (line 5484) | function actualDisplay( name, doc ) {
  function defaultDisplay (line 5506) | function defaultDisplay( nodeName ) {
  function curCSS (line 5553) | function curCSS( elem, name, computed ) {
  function addGetHookIf (line 5601) | function addGetHookIf( conditionFn, hookFn ) {
  function computePixelPositionAndBoxSizingReliable (line 5641) | function computePixelPositionAndBoxSizingReliable() {
  function vendorPropName (line 5746) | function vendorPropName( style, name ) {
  function setPositiveNumber (line 5768) | function setPositiveNumber( elem, value, subtract ) {
  function augmentWidthOrHeight (line 5776) | function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
  function getWidthOrHeight (line 5815) | function getWidthOrHeight( elem, name, extra ) {
  function showHide (line 5859) | function showHide( elements, show ) {
  function Tween (line 6157) | function Tween( elem, options, prop, end, easing ) {
  function createFxNow (line 6326) | function createFxNow() {
  function genFx (line 6334) | function genFx( type, includeWidth ) {
  function createTween (line 6354) | function createTween( value, prop, animation ) {
  function defaultPrefilter (line 6368) | function defaultPrefilter( elem, props, opts ) {
  function propFilter (line 6501) | function propFilter( props, specialEasing ) {
  function Animation (line 6538) | function Animation( elem, properties, options ) {
  function addToPrefiltersOrTransports (line 7586) | function addToPrefiltersOrTransports( structure ) {
  function inspectPrefiltersOrTransports (line 7618) | function inspectPrefiltersOrTransports( structure, options, originalOpti...
  function ajaxExtend (line 7645) | function ajaxExtend( target, src ) {
  function ajaxHandleResponses (line 7665) | function ajaxHandleResponses( s, jqXHR, responses ) {
  function ajaxConvert (line 7721) | function ajaxConvert( s, response, jqXHR, isSuccess ) {
  function done (line 8179) | function done( status, nativeStatusText, responses, headers ) {
  function buildParams (line 8423) | function buildParams( prefix, obj, traditional, add ) {
  function getWindow (line 8917) | function getWindow( elem ) {

FILE: resources/public/js/jquery.console.js
  function newPromptBox (line 234) | function newPromptBox() {
  function isIgnorableKey (line 349) | function isIgnorableKey(e) {
  function rotateHistory (line 357) | function rotateHistory(n){
  function previousHistory (line 380) | function previousHistory() {
  function nextHistory (line 384) | function nextHistory() {
  function addToHistory (line 389) | function addToHistory(line){
  function deleteCharAtPos (line 395) | function deleteCharAtPos(){
  function backDelete (line 405) | function backDelete() {
  function forwardDelete (line 412) | function forwardDelete() {
  function deleteUntilEnd (line 418) | function deleteUntilEnd() {
  function deleteNextWord (line 424) | function deleteNextWord() {
  function newLine (line 445) | function newLine() {
  function commandTrigger (line 457) | function commandTrigger() {
  function scrollToBottom (line 474) | function scrollToBottom() {
  function cancelExecution (line 488) | function cancelExecution() {
  function handleCommand (line 496) | function handleCommand() {
  function disableInput (line 534) | function disableInput() {
  function enableInput (line 539) | function enableInput() {
  function commandResult (line 545) | function commandResult(msg,className) {
  function report (line 563) | function report(msg,className) {
  function message (line 572) | function message(msg,className) {
  function moveColumn (line 598) | function moveColumn(n){
  function moveForward (line 605) | function moveForward() {
  function moveBackward (line 613) | function moveBackward() {
  function moveToStart (line 621) | function moveToStart() {
  function moveToEnd (line 626) | function moveToEnd() {
  function moveToNextWord (line 631) | function moveToNextWord() {
  function moveToPreviousWord (line 644) | function moveToPreviousWord() {
  function isCharAlphanumeric (line 659) | function isCharAlphanumeric(charToTest) {
  function doComplete (line 669) | function doComplete() {
  function doNothing (line 703) | function doNothing() {}
  function updatePromptDisplay (line 716) | function updatePromptDisplay(){
  function htmlEncode (line 748) | function htmlEncode(text){

FILE: resources/public/js/show_updates.js
  function updateCueGrid (line 1) | function updateCueGrid( data ) {
  function updateEffectState (line 9) | function updateEffectState() {
  function buildFraction (line 19) | function buildFraction( n ) {
  function buildEffectRow (line 24) | function buildEffectRow( data ) {
  function sendCueVarUpdate (line 120) | function sendCueVarUpdate( effectKey, id, varKey, value ) {
  function createCueVarRow (line 133) | function createCueVarRow( data, varSpec, element ) {
  function findOrCreateCueVarSlider (line 147) | function findOrCreateCueVarSlider( data, varSpec ) {
  function findOrCreateCheckbox (line 191) | function findOrCreateCheckbox (data, varSpec ) {
  function findOrCreateColorPicker (line 215) | function findOrCreateColorPicker( data, varSpec ) {
  function processCueVarChange (line 241) | function processCueVarChange( data ) {
  function processEffectUpdate (line 271) | function processEffectUpdate( data ) {
  function updateEffectList (line 319) | function updateEffectList( data ) {
  function updateGrandMaster (line 328) | function updateGrandMaster( data ) {
  function grandMasterSlideStart (line 334) | function grandMasterSlideStart( eventObject ) {
  function sendGrandMasterUpdate (line 338) | function sendGrandMasterUpdate( control_id, value ) {
  function grandMasterSlide (line 346) | function grandMasterSlide( eventObject ) {
  function grandMasterSlideStop (line 350) | function grandMasterSlideStop( eventObject ) {
  function updateButtons (line 355) | function updateButtons( data ) {
  function updateTapLabel (line 365) | function updateTapLabel( ) {
  function updateMetronome (line 385) | function updateMetronome( data ) {
  function updateLinkMenu (line 433) | function updateLinkMenu( data ) {
  function updateSyncMenu (line 445) | function updateSyncMenu( data ) {
  function updateLoad (line 450) | function updateLoad( data ) {
  function updateStatus (line 477) | function updateStatus( data ) {
  function updateShow (line 503) | function updateShow() {
  function uiButtonClicked (line 566) | function uiButtonClicked( eventObject ) {
  function errorDetailsClicked (line 574) | function errorDetailsClicked( eventObject ) {
  function metronomeAdjustClicked (line 578) | function metronomeAdjustClicked( eventObject ) {
  function cueCellClicked (line 588) | function cueCellClicked( eventObject ) {
  function deleteCue (line 642) | function deleteCue( cell ) {
  function linkMenuChanged (line 656) | function linkMenuChanged( eventObject ) {
  function syncMenuChosen (line 664) | function syncMenuChosen( eventObject ) {
  function makeMacroChosen (line 675) | function makeMacroChosen (eventObject) {
  function bpmSlideStart (line 692) | function bpmSlideStart( eventObject ) {
  function sendBpmUpdate (line 696) | function sendBpmUpdate( control_id, value ) {
  function bpmSlide (line 704) | function bpmSlide( eventObject ) {
  function bpmSlideStop (line 708) | function bpmSlideStop( eventObject ) {
  function decorateMetronomeAdjusters (line 713) | function decorateMetronomeAdjusters( selector, onMouseDown ) {
  function checkShiftKey (line 729) | function checkShiftKey( eventObject ) {
  function fakeScrollClick (line 738) | function fakeScrollClick ( direction, eventObject ) {
  function fakeTempoTap (line 748) | function fakeTempoTap( eventObject ) {

FILE: resources/public/js/web-repl.js
  function sendCommand (line 1) | function sendCommand(line) {
  function updateSize (line 25) | function updateSize() {
Condensed preview — 169 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,977K chars).
[
  {
    "path": ".gitattributes",
    "chars": 34,
    "preview": "api-doc/* -diff linguist-vendored\n"
  },
  {
    "path": ".github/scripts/build.sh",
    "chars": 713,
    "preview": "#!/bin/bash\n\n\n# This script is run by GitHub Actions to build the cross-platform uberjar.\n\n# If this is a full release, "
  },
  {
    "path": ".github/scripts/build_guide.sh",
    "chars": 917,
    "preview": "#!/usr/bin/env bash\n\n# This script is run by GitHub Actions to build the\n# Antora site hosting the developer guide.\n\nset"
  },
  {
    "path": ".github/workflows/uberjar.yml",
    "chars": 3376,
    "preview": "name: Create überjar\n\non:\n  push:\n    branches:\n      - main\n\nenv:\n  initial_description: |\n    :construction: This is p"
  },
  {
    "path": ".gitignore",
    "chars": 287,
    "preview": "*.class\n*.jar\n.hg/\n.hgignore\n/.cider_history\n/.dir-locals.el\n/.env\n/.lein-*\n/.nrepl-port\n/checkouts\n/classes\n/doc/build\n"
  },
  {
    "path": ".htmltest.yml",
    "chars": 180,
    "preview": "DirectoryPath: \"doc/build/site\"\nIgnoreURLs:\n- \"github.com/Deep-Symmetry/afterglow/blob/\"\n- \"support.native-instruments.c"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 36847,
    "preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nThis change log follows the conventio"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3219,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 4962,
    "preview": "# Contributing\n\nHi there! We're delighted that you'd like to contribute to this\nproject. It has been generous collaborat"
  },
  {
    "path": "LICENSE",
    "chars": 14198,
    "preview": "Eclipse Public License - v 2.0\n\n    THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE\n    PUBLIC LICE"
  },
  {
    "path": "README.md",
    "chars": 51706,
    "preview": "# Afterglow\n\n[![project chat](https://img.shields.io/badge/chat-on%20zulip-brightgreen)](https://deep-symmetry.zulipchat"
  },
  {
    "path": "doc/README.md",
    "chars": 962,
    "preview": "# Developer Guide Module\n\n> :mag_right: If you are looking for the online documentation, it has\n> [moved](https://afterg"
  },
  {
    "path": "doc/antora.yml",
    "chars": 258,
    "preview": "name: afterglow\ntitle: Afterglow\nversion: ~\ndisplay_version: 'main'\nprerelease: true\nstart_page: ROOT:README.adoc\nasciid"
  },
  {
    "path": "doc/ds.yml",
    "chars": 566,
    "preview": "site:\n  title: Afterglow Developer Guide\n  url: https://deepsymmetry.org/afterglow/guide\n  start_page: afterglow::README"
  },
  {
    "path": "doc/embedded.yml",
    "chars": 485,
    "preview": "site:\n  title: Afterglow Developer Guide\n  url: http:/guide\n  start_page: afterglow::README.adoc\ncontent:\n  edit_url: fa"
  },
  {
    "path": "doc/github-actions.yml",
    "chars": 653,
    "preview": "site:\n  title: Afterglow Developer Guide\n  url: https://afterglow-guide.deepsymmetry.org/\n  start_page: afterglow::READM"
  },
  {
    "path": "doc/modules/ROOT/assets/source/Afterglow logo.ai",
    "chars": 317505,
    "preview": "%PDF-1.5\r%\r\n1 0 obj\r<</Metadata 2 0 R/OCProperties<</D<</ON[5 0 R 31 0 R 81 0 R 131 0 R]/Order 132 0 R/RBGroups[]>>/OCGs"
  },
  {
    "path": "doc/modules/ROOT/assets/source/OLA Logo.ai",
    "chars": 161227,
    "preview": "%PDF-1.5\r%\r\n1 0 obj\r<</Metadata 2 0 R/OCProperties<</D<</ON[5 0 R]/Order 6 0 R/RBGroups[]>>/OCGs[5 0 R]>>/Pages 3 0 R/Ty"
  },
  {
    "path": "doc/modules/ROOT/nav.adoc",
    "chars": 571,
    "preview": "* xref:README.adoc[Developer Guide]\n* xref:cues.adoc[Cues]\n* xref:effects.adoc[Effects]\n* xref:parameters.adoc[Dynamic P"
  },
  {
    "path": "doc/modules/ROOT/pages/README.adoc",
    "chars": 32026,
    "preview": "= Afterglow Developer Guide\nJames Elliott <james@deepsymmetry.org>\n\n[[organization]] This section provides an\n<<introduc"
  },
  {
    "path": "doc/modules/ROOT/pages/color.adoc",
    "chars": 6193,
    "preview": "= Working with Color\nJames Elliott <james@deepsymmetry.org>\n\nCues that assign color to lights are designed to leverage t"
  },
  {
    "path": "doc/modules/ROOT/pages/cues.adoc",
    "chars": 39207,
    "preview": "= Cues\nJames Elliott <james@deepsymmetry.org>\n\nCues are designed to support creating user interfaces for controlling\neff"
  },
  {
    "path": "doc/modules/ROOT/pages/effects.adoc",
    "chars": 58922,
    "preview": "= Effects\nJames Elliott <james@deepsymmetry.org>\n\nEffects determine what a light or group of lights are doing at a given"
  },
  {
    "path": "doc/modules/ROOT/pages/fixture_definitions.adoc",
    "chars": 62021,
    "preview": "= Fixture Definitions\nJames Elliott <james@deepsymmetry.org>\n\n// TODO: Can the following be defived from Antora, or just"
  },
  {
    "path": "doc/modules/ROOT/pages/launchpad.adoc",
    "chars": 12385,
    "preview": "= Using the Novation Launchpad Family\nJames Elliott <james@deepsymmetry.org>\n\nNovation makes an entire family of grid co"
  },
  {
    "path": "doc/modules/ROOT/pages/mapping_sync.adoc",
    "chars": 39901,
    "preview": "= MIDI Mapping and Beat Sync\nJames Elliott <james@deepsymmetry.org>\n\nAfterglow is designed to work with MIDI controllers"
  },
  {
    "path": "doc/modules/ROOT/pages/metronomes.adoc",
    "chars": 7433,
    "preview": "= Metronomes\nJames Elliott <james@deepsymmetry.org>\n\nMetronomes play a fundamental role in coordinating the timing of\nAf"
  },
  {
    "path": "doc/modules/ROOT/pages/oscillators.adoc",
    "chars": 16016,
    "preview": "= Oscillators\nJames Elliott <james@deepsymmetry.org>\n\nOscillators in Afterglow are a flexible way of turning the timing\n"
  },
  {
    "path": "doc/modules/ROOT/pages/parameters.adoc",
    "chars": 31453,
    "preview": "= Dynamic Parameters\nJames Elliott <james@deepsymmetry.org>\n\nDynamic parameters provide a way to turn a simple cue into "
  },
  {
    "path": "doc/modules/ROOT/pages/push.adoc",
    "chars": 23805,
    "preview": "= Using Ableton Push\nJames Elliott <james@deepsymmetry.org>\n\nSome controllers have such rich capabilities that they dese"
  },
  {
    "path": "doc/modules/ROOT/pages/push2.adoc",
    "chars": 27535,
    "preview": "= Using Ableton Push 2\nJames Elliott <james@deepsymmetry.org>\n\nSome controllers have such rich capabilities that they de"
  },
  {
    "path": "doc/modules/ROOT/pages/rendering_loop.adoc",
    "chars": 22732,
    "preview": "= The Rendering Loop\nJames Elliott <james@deepsymmetry.org>\n\nThis page contains advanced, low-level information for peop"
  },
  {
    "path": "doc/modules/ROOT/pages/show_space.adoc",
    "chars": 3776,
    "preview": "= Show Space\nJames Elliott <james@deepsymmetry.org>\n\nIn order to be able to create spatial effects, from lighting gradie"
  },
  {
    "path": "doc/modules/ROOT/pages/videos.adoc",
    "chars": 1055,
    "preview": "= Videos\nJames Elliott <james@deepsymmetry.org>\n\nThis page collects performance videos that highlight Afterglow in\nactio"
  },
  {
    "path": "doc/primes.md",
    "chars": 21247,
    "preview": "# Playing with Primes in Clojure\n\nMy friend Andrew recently dicovered the joys of programming, and\nshared his cool Pytho"
  },
  {
    "path": "logs/README.txt",
    "chars": 482,
    "preview": "This directory is where development-mode log files are written, to\nmake it easy to debug problems which happen on backgr"
  },
  {
    "path": "package.json",
    "chars": 745,
    "preview": "{\n  \"private\": true,\n  \"description\": \"Used to build the documentation site, not the project itself.\",\n  \"dependencies\":"
  },
  {
    "path": "project.clj",
    "chars": 6104,
    "preview": "(defproject afterglow :lein-v\n  :description \"A live-coding environment for light shows, built on the Open Lighting Arch"
  },
  {
    "path": "resources/afterglow/readme.txt",
    "chars": 107,
    "preview": "This file is here to make sure Git creates the parent directory,\nwhich is needed for building the uberjar.\n"
  },
  {
    "path": "resources/docs/docs.md",
    "chars": 1456,
    "preview": "<div class=\"bs-callout bs-callout-danger\">\n\n### Database Configuration is Required\n\nBefore continuing please follow the "
  },
  {
    "path": "resources/public/css/bootstrap-cyborg.css",
    "chars": 150499,
    "preview": "@import url(\"https://fonts.googleapis.com/css?family=Roboto:400,700\");\n/*!\n * bootswatch v3.3.5\n * Homepage: http://boot"
  },
  {
    "path": "resources/public/css/bootstrap-slider.css",
    "chars": 8239,
    "preview": "/*! =======================================================\n                      VERSION  6.0.10              \n========"
  },
  {
    "path": "resources/public/css/bootstrap-switch.css",
    "chars": 6993,
    "preview": "/* ========================================================================\n * bootstrap-switch - v3.3.2\n * http://www.b"
  },
  {
    "path": "resources/public/css/bootstrap-theme.css",
    "chars": 26132,
    "preview": "/*!\n * Bootstrap v3.3.5 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://gi"
  },
  {
    "path": "resources/public/css/bootstrap.css",
    "chars": 147430,
    "preview": "/*!\n * Bootstrap v3.3.5 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://gi"
  },
  {
    "path": "resources/public/css/jquery.minicolors.css",
    "chars": 9854,
    "preview": ".minicolors {\n  position: relative;\n}\n\n.minicolors-sprite {\n  background-image: url(jquery.minicolors.png);\n}\n\n.minicolo"
  },
  {
    "path": "resources/public/css/screen.css",
    "chars": 225,
    "preview": "html,\nbody {\n    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;\n    height: 100%;\n    padding-top: 35px;\n}"
  },
  {
    "path": "resources/public/css/show.css",
    "chars": 2611,
    "preview": "td.cue-cell {\n    width: 90px;\n    max-width: 100px;\n    height: 50px;\n    max-height: 60px;\n    text-align: center;\n   "
  },
  {
    "path": "resources/public/css/web-repl.css",
    "chars": 1058,
    "preview": "#console {\n    background: #222222;\n    color: #888888;\n    margin: 10px;\n    border-radius: 5px;\n    -moz-border-radius"
  },
  {
    "path": "resources/public/epl-v10.html",
    "chars": 12637,
    "preview": "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www."
  },
  {
    "path": "resources/public/font-awesome-4.5.0/HELP-US-OUT.txt",
    "chars": 318,
    "preview": "I hope you love Font Awesome. If you've found it useful, please do me a favor and check out my latest project,\nFonticons"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/css/font-awesome.css",
    "chars": 33233,
    "preview": "/*!\n *  Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome\n *  License - http://fontawesome.io/lice"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/less/animated.less",
    "chars": 713,
    "preview": "// Animated Icons\n// --------------------------\n\n.@{fa-css-prefix}-spin {\n  -webkit-animation: fa-spin 2s infinite linea"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/less/bordered-pulled.less",
    "chars": 585,
    "preview": "// Bordered & Pulled\n// -------------------------\n\n.@{fa-css-prefix}-border {\n  padding: .2em .25em .15em;\n  border: sol"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/less/core.less",
    "chars": 452,
    "preview": "// Base Class Definition\n// -------------------------\n\n.@{fa-css-prefix} {\n  display: inline-block;\n  font: normal norma"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/less/fixed-width.less",
    "chars": 119,
    "preview": "// Fixed Width Icons\n// -------------------------\n.@{fa-css-prefix}-fw {\n  width: (18em / 14);\n  text-align: center;\n}\n"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/less/font-awesome.less",
    "chars": 465,
    "preview": "/*!\n *  Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome\n *  License - http://fontawesome.io/lice"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/less/icons.less",
    "chars": 43859,
    "preview": "/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen\n   readers do not read off random characters th"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/less/larger.less",
    "chars": 370,
    "preview": "// Icon Sizes\n// -------------------------\n\n/* makes the font 33% larger relative to the icon container */\n.@{fa-css-pre"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/less/list.less",
    "chars": 377,
    "preview": "// List Icons\n// -------------------------\n\n.@{fa-css-prefix}-ul {\n  padding-left: 0;\n  margin-left: @fa-li-width;\n  lis"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/less/mixins.less",
    "chars": 926,
    "preview": "// Mixins\n// --------------------------\n\n.fa-icon() {\n  display: inline-block;\n  font: normal normal normal @fa-font-siz"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/less/path.less",
    "chars": 770,
    "preview": "/* FONT PATH\n * -------------------------- */\n\n@font-face {\n  font-family: 'FontAwesome';\n  src: url('@{fa-font-path}/fo"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/less/rotated-flipped.less",
    "chars": 622,
    "preview": "// Rotated & Flipped Icons\n// -------------------------\n\n.@{fa-css-prefix}-rotate-90  { .fa-icon-rotate(90deg, 1);  }\n.@"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/less/stacked.less",
    "chars": 476,
    "preview": "// Stacked Icons\n// -------------------------\n\n.@{fa-css-prefix}-stack {\n  position: relative;\n  display: inline-block;\n"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/less/variables.less",
    "chars": 19778,
    "preview": "// Variables\n// --------------------------\n\n@fa-font-path:        \"../fonts\";\n@fa-font-size-base:   14px;\n@fa-line-heigh"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/scss/_animated.scss",
    "chars": 715,
    "preview": "// Spinning Icons\n// --------------------------\n\n.#{$fa-css-prefix}-spin {\n  -webkit-animation: fa-spin 2s infinite line"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/scss/_bordered-pulled.scss",
    "chars": 592,
    "preview": "// Bordered & Pulled\n// -------------------------\n\n.#{$fa-css-prefix}-border {\n  padding: .2em .25em .15em;\n  border: so"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/scss/_core.scss",
    "chars": 459,
    "preview": "// Base Class Definition\n// -------------------------\n\n.#{$fa-css-prefix} {\n  display: inline-block;\n  font: normal norm"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/scss/_fixed-width.scss",
    "chars": 120,
    "preview": "// Fixed Width Icons\n// -------------------------\n.#{$fa-css-prefix}-fw {\n  width: (18em / 14);\n  text-align: center;\n}\n"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/scss/_icons.scss",
    "chars": 44553,
    "preview": "/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen\n   readers do not read off random characters th"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/scss/_larger.scss",
    "chars": 375,
    "preview": "// Icon Sizes\n// -------------------------\n\n/* makes the font 33% larger relative to the icon container */\n.#{$fa-css-pr"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/scss/_list.scss",
    "chars": 378,
    "preview": "// List Icons\n// -------------------------\n\n.#{$fa-css-prefix}-ul {\n  padding-left: 0;\n  margin-left: $fa-li-width;\n  li"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/scss/_mixins.scss",
    "chars": 946,
    "preview": "// Mixins\n// --------------------------\n\n@mixin fa-icon() {\n  display: inline-block;\n  font: normal normal normal #{$fa-"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/scss/_path.scss",
    "chars": 783,
    "preview": "/* FONT PATH\n * -------------------------- */\n\n@font-face {\n  font-family: 'FontAwesome';\n  src: url('#{$fa-font-path}/f"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/scss/_rotated-flipped.scss",
    "chars": 672,
    "preview": "// Rotated & Flipped Icons\n// -------------------------\n\n.#{$fa-css-prefix}-rotate-90  { @include fa-icon-rotate(90deg, "
  },
  {
    "path": "resources/public/font-awesome-4.5.0/scss/_stacked.scss",
    "chars": 482,
    "preview": "// Stacked Icons\n// -------------------------\n\n.#{$fa-css-prefix}-stack {\n  position: relative;\n  display: inline-block;"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/scss/_variables.scss",
    "chars": 19859,
    "preview": "// Variables\n// --------------------------\n\n$fa-font-path:        \"../fonts\" !default;\n$fa-font-size-base:   14px !defau"
  },
  {
    "path": "resources/public/font-awesome-4.5.0/scss/font-awesome.scss",
    "chars": 405,
    "preview": "/*!\n *  Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome\n *  License - http://fontawesome.io/lice"
  },
  {
    "path": "resources/public/fonts/Lekton/SIL Open Font License.txt",
    "chars": 4299,
    "preview": "This Font Software is licensed under the SIL Open Font License, Version 1.1.\nThis license is copied below, and is also a"
  },
  {
    "path": "resources/public/fonts/Open_Sans_Condensed/LICENSE.txt",
    "chars": 11560,
    "preview": "\r\n                                 Apache License\r\n                           Version 2.0, January 2004\r\n               "
  },
  {
    "path": "resources/public/fonts/Roboto/LICENSE.txt",
    "chars": 11560,
    "preview": "\r\n                                 Apache License\r\n                           Version 2.0, January 2004\r\n               "
  },
  {
    "path": "resources/public/js/BootstrapMenu.js",
    "chars": 71497,
    "preview": "/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n\n/*"
  },
  {
    "path": "resources/public/js/bootstrap.js",
    "chars": 68890,
    "preview": "/*!\n * Bootstrap v3.3.5 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under the MIT license"
  },
  {
    "path": "resources/public/js/jquery-2.1.4.js",
    "chars": 247597,
    "preview": "/*!\n * jQuery JavaScript Library v2.1.4\n * http://jquery.com/\n *\n * Includes Sizzle.js\n * http://sizzlejs.com/\n *\n * Cop"
  },
  {
    "path": "resources/public/js/jquery.console.js",
    "chars": 22251,
    "preview": "// JQuery Console 1.0\n// Sun Feb 21 20:28:47 GMT 2010\n//\n// Copyright 2010 Chris Done, Simon David Pratt. All rights res"
  },
  {
    "path": "resources/public/js/jquery.websocket-0.0.1.js",
    "chars": 1259,
    "preview": "/*\n * jQuery Web Sockets Plugin v0.0.1\n * http://code.google.com/p/jquery-websocket/\n *\n * This document is licensed as "
  },
  {
    "path": "resources/public/js/npm.js",
    "chars": 484,
    "preview": "// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.\nrequ"
  },
  {
    "path": "resources/public/js/show_updates.js",
    "chars": 29337,
    "preview": "function updateCueGrid( data ) {\n    $.each( data, function( key, val ) {\n        $('#' + val.id).css('background-color'"
  },
  {
    "path": "resources/public/js/web-repl.js",
    "chars": 2009,
    "preview": "function sendCommand(line) {\n    var jqxhr = $.post( (context + \"/console\"),\n                        { \"command\": line,\n"
  },
  {
    "path": "resources/public/shaders/vertex.glsl",
    "chars": 202,
    "preview": "// switch on high precision floats\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nvoid main()\n{\n  vec4 mvPosition = modelVi"
  },
  {
    "path": "resources/templates/about.html",
    "chars": 141,
    "preview": "{% extends \"base.html\" %}\n{% block content %}\n  <p>To  be filled in, or removed. The embedded user guide is coming first"
  },
  {
    "path": "resources/templates/base.html",
    "chars": 2120,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n    <title>{{tit"
  },
  {
    "path": "resources/templates/console.html",
    "chars": 247,
    "preview": "{% extends \"base.html\" %}\n{% block content %}\n<div class=\"row\" id=\"console\"/>\n{% endblock %}\n{% block page-scripts %}\n  "
  },
  {
    "path": "resources/templates/cue_grid.html",
    "chars": 179,
    "preview": "{% for row in grid %}\n      <tr>{% for cue in row %}\n        <td class=\"cue-cell\" id=\"{{cue.id}}\" {{cue.style-color|safe"
  },
  {
    "path": "resources/templates/current-scene-fragment.js",
    "chars": 801,
    "preview": "      iNumSpots:   { type: 'i', value: {{ count }} },\n      iSpotPosition: { type: 'v3v', value: [ \n        {% for pos i"
  },
  {
    "path": "resources/templates/current-scene.json",
    "chars": 78,
    "preview": "{\n    timestamp: {{ timestamp }},\n{% include \"current-scene-fragment.js\" %}\n}\n"
  },
  {
    "path": "resources/templates/error.html",
    "chars": 1629,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n    <title>Something bad happened</title>\n    <meta http-equiv=\"Content-Type\" content=\"tex"
  },
  {
    "path": "resources/templates/fixture-definition.clj.template",
    "chars": 4278,
    "preview": "(ns afterglow.fixtures.{{manufacturer|sanitize}}\n  \"Translated definition for the fixture {{model}}\n  from {{manufacture"
  },
  {
    "path": "resources/templates/fragment.glsl",
    "chars": 4576,
    "preview": "#ifdef GL_ES\nprecision highp float;\n#endif\n\t\t\nconst int MAX_SPOTS = {{max-lights}};\nconst float SCALE = {{scale}};\n\nunif"
  },
  {
    "path": "resources/templates/home.html",
    "chars": 1899,
    "preview": "{% extends \"base.html\" %}\n{% block content %}\n  <div class=\"jumbotron\">\n    <img style=\"float:right\" src=\"/img/Afterglow"
  },
  {
    "path": "resources/templates/link_menu.html",
    "chars": 242,
    "preview": "        Linked controller:<br/>\n        <select id=\"link-select\">{% for element in link-menu %}\n          <option value="
  },
  {
    "path": "resources/templates/show.html",
    "chars": 8973,
    "preview": "{% extends \"base.html\" %}\n\n{% block page-nav-items %}\n            <li class=\"navbar-text\">Load: <canvas id=\"loadBar\" wid"
  },
  {
    "path": "resources/templates/sync_menu.html",
    "chars": 537,
    "preview": "              <p>Establish a BPM or beat synchronization source for this show:\n{% for element in sync-menu %}           "
  },
  {
    "path": "resources/templates/visualizer.html",
    "chars": 4514,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n    <style>\n    "
  },
  {
    "path": "src/afterglow/beyond.clj",
    "chars": 9540,
    "preview": "(ns afterglow.beyond\n  \"Provides the ability to communicate with Pangolin's Beyond laser\n  show software, including sync"
  },
  {
    "path": "src/afterglow/carabiner.clj",
    "chars": 16625,
    "preview": "(ns afterglow.carabiner\n  \"Provides synchronization with Ableton Link on the local network,\n  using the lib-carabiner li"
  },
  {
    "path": "src/afterglow/channels.clj",
    "chars": 17704,
    "preview": "(ns afterglow.channels\n  \"Functions for modeling DMX channels\"\n  {:author \"James Elliott\"}\n  (:require [afterglow.fixtur"
  },
  {
    "path": "src/afterglow/controllers/ableton_push.clj",
    "chars": 105966,
    "preview": "(ns afterglow.controllers.ableton-push\n  \"Allows the Ableton Push to be used as a control surface for\n  Afterglow. Its f"
  },
  {
    "path": "src/afterglow/controllers/ableton_push_2.clj",
    "chars": 153062,
    "preview": "(ns afterglow.controllers.ableton-push-2\n  \"Allows the Ableton Push 2 to be used as a control surface for\n  Afterglow. I"
  },
  {
    "path": "src/afterglow/controllers/color.clj",
    "chars": 4792,
    "preview": "(ns afterglow.controllers.color\n  \"Provides support for adjusting components of a show variable\n  containing a color usi"
  },
  {
    "path": "src/afterglow/controllers/launchpad_mini.clj",
    "chars": 28433,
    "preview": "(ns afterglow.controllers.launchpad-mini\n  \"Allows the Novation Launchpad Mini and Launchpad S to be used as\n  control s"
  },
  {
    "path": "src/afterglow/controllers/launchpad_mk2.clj",
    "chars": 29378,
    "preview": "(ns afterglow.controllers.launchpad-mk2\n    \"Allows the Novation Launchpad Mk2 to be used as a control surface\n  for Aft"
  },
  {
    "path": "src/afterglow/controllers/launchpad_pro.clj",
    "chars": 36093,
    "preview": "(ns afterglow.controllers.launchpad-pro\n  \"Allows the Novation Launchpad Pro to be used as a control surface\n  for After"
  },
  {
    "path": "src/afterglow/controllers/tempo.clj",
    "chars": 11915,
    "preview": "(ns afterglow.controllers.tempo\n  \"Provides support for easily implementing tap-tempo and shift\n  buttons on any MIDI co"
  },
  {
    "path": "src/afterglow/controllers.clj",
    "chars": 43173,
    "preview": "(ns afterglow.controllers\n  \"Provides shared services for all controller implementations.\"\n  {:author \"James Elliott\"}\n "
  },
  {
    "path": "src/afterglow/core.clj",
    "chars": 12693,
    "preview": "(ns afterglow.core\n  \"This is the main class for running Afterglow as a self-contained JAR application.\n  When you are l"
  },
  {
    "path": "src/afterglow/coremidi4j.clj",
    "chars": 792,
    "preview": "(ns afterglow.coremidi4j\n  (:import [uk.co.xfactorylibrarians.coremidi4j CoreMidiDeviceProvider CoreMidiNotification]))\n"
  },
  {
    "path": "src/afterglow/dj_link.clj",
    "chars": 7784,
    "preview": "(ns afterglow.dj-link\n  \"Provides synchronization with equipment sending Pioneer Pro DJ Link\n  packets on the local netw"
  },
  {
    "path": "src/afterglow/effects/channel.clj",
    "chars": 14383,
    "preview": "(ns afterglow.effects.channel\n  \"Effects pipeline functions for working with individual DMX channels.\"\n  {:author \"James"
  },
  {
    "path": "src/afterglow/effects/color.clj",
    "chars": 11382,
    "preview": "(ns afterglow.effects.color\n  \"Effects pipeline functions for working with color assignments to\n  fixtures and heads.\"\n "
  },
  {
    "path": "src/afterglow/effects/cues.clj",
    "chars": 33982,
    "preview": "(ns afterglow.effects.cues\n  \"Cues provide a user interface for controlling effects, by\n  associating them with cells in"
  },
  {
    "path": "src/afterglow/effects/dimmer.clj",
    "chars": 11248,
    "preview": "(ns afterglow.effects.dimmer\n  \"Effects pipeline functions for working with dimmer channels for\n  fixtures and heads. Di"
  },
  {
    "path": "src/afterglow/effects/fun.clj",
    "chars": 57320,
    "preview": "(ns afterglow.effects.fun\n  \"A collection of neat effects that are both useful in shows, and\n  examples of how to create"
  },
  {
    "path": "src/afterglow/effects/movement.clj",
    "chars": 12827,
    "preview": "(ns afterglow.effects.movement\n  \"Effects pipeline functions for working with direction assignments\n  to fixtures and he"
  },
  {
    "path": "src/afterglow/effects/oscillators.clj",
    "chars": 23242,
    "preview": "(ns afterglow.effects.oscillators\n  \"Provide a variety of waveforms at frequencies related to the show metronome to\n  fa"
  },
  {
    "path": "src/afterglow/effects/params.clj",
    "chars": 52660,
    "preview": "(ns afterglow.effects.params\n  \"A general mechanism for passing dynamic parameters to effect\n  functions and assigners a"
  },
  {
    "path": "src/afterglow/effects/show_variable.clj",
    "chars": 5763,
    "preview": "(ns afterglow.effects.show-variable\n  \"Virtual effects which set a value in a show variable while they\n  are running. Pa"
  },
  {
    "path": "src/afterglow/effects.clj",
    "chars": 30047,
    "preview": "(ns afterglow.effects\n  \"Support functions for building the effects pipeline.\"\n  {:author \"James Elliott\"}\n  (:require ["
  },
  {
    "path": "src/afterglow/examples.clj",
    "chars": 142532,
    "preview": "(ns afterglow.examples\n  \"Show some simple ways to use Afterglow, and hopefully inspire\n  exploration.\" {:author \"James "
  },
  {
    "path": "src/afterglow/fixtures/american_dj.clj",
    "chars": 1160,
    "preview": "(ns afterglow.fixtures.american-dj\n  \"Definitions for fixtures provided by [American DJ](http://adj.com).\"\n  {:author \"J"
  },
  {
    "path": "src/afterglow/fixtures/blizzard.clj",
    "chars": 25215,
    "preview": "(ns afterglow.fixtures.blizzard\n  \"Definitions for fixtures provided by [Blizzard\n  Lighting](https://www.blizzardpro.co"
  },
  {
    "path": "src/afterglow/fixtures/chauvet.clj",
    "chars": 63918,
    "preview": "(ns afterglow.fixtures.chauvet\n  \"Models for fixtures provided by [Chauvet Lighting](http://www.chauvetlighting.com).\"\n "
  },
  {
    "path": "src/afterglow/fixtures/qxf.clj",
    "chars": 16976,
    "preview": "(ns afterglow.fixtures.qxf\n  \"Functions to work with Fixture Definition Files from\n  the [QLC+](http://www.qlcplus.org/)"
  },
  {
    "path": "src/afterglow/fixtures.clj",
    "chars": 3721,
    "preview": "(ns afterglow.fixtures\n  \"Utility functions common to fixture definitions.\"\n  {:author \"James Elliott\"}\n  (:require [aft"
  },
  {
    "path": "src/afterglow/init.clj",
    "chars": 270,
    "preview": "(ns afterglow.init\n  \"This namespace is the context in which any init-files specified on\n  the command line will be load"
  },
  {
    "path": "src/afterglow/midi.clj",
    "chars": 53752,
    "preview": "(ns afterglow.midi\n  \"Handles MIDI communication, including syncing a show metronome to MIDI clock pulses.\"\n  (:require "
  },
  {
    "path": "src/afterglow/rhythm.clj",
    "chars": 16821,
    "preview": "(ns afterglow.rhythm\n  \"Functions to help work with musical time, evolved from the original\n  version in [Overtone](http"
  },
  {
    "path": "src/afterglow/show.clj",
    "chars": 62119,
    "preview": "(ns afterglow.show\n  \"Encapsulates a synchronized light show, executing a varying\n  collection of effects with output to"
  },
  {
    "path": "src/afterglow/show_context.clj",
    "chars": 1690,
    "preview": "(ns afterglow.show-context\n  \"Establishes a notion of the _current show_ using the dynamic var\n  `*show*`, to save havin"
  },
  {
    "path": "src/afterglow/shows/chris.clj",
    "chars": 50276,
    "preview": "(ns afterglow.shows.chris\n  \"Cues for shows Chris wants to run for Rhett. Useful as an example of\n  how a small show run"
  },
  {
    "path": "src/afterglow/shows/sallie.clj",
    "chars": 28110,
    "preview": "(ns afterglow.shows.sallie\n  \"Cues for Sallie's birthday/housewarming party. Useful as an example\n  of how an actual sma"
  },
  {
    "path": "src/afterglow/shows/wedding.clj",
    "chars": 86275,
    "preview": "(ns afterglow.shows.wedding\n  \"Cues for Joey and Courtney's wedding reception. Useful as an example\n  of how an actual s"
  },
  {
    "path": "src/afterglow/transform.clj",
    "chars": 20124,
    "preview": "(ns afterglow.transform\n  \"Functions for modeling light position and rotation. If you want to\n  make use of Afterglow's "
  },
  {
    "path": "src/afterglow/util.clj",
    "chars": 3757,
    "preview": "(ns afterglow.util\n  \"Utility functions that are likely to be widely useful\"\n  {:author \"James Elliott\"}\n  (:require [cl"
  },
  {
    "path": "src/afterglow/version.clj",
    "chars": 1033,
    "preview": "(ns afterglow.version\n  \"Allows the runtime environment to determine the project name and\n  version which built it.\"\n  ("
  },
  {
    "path": "src/afterglow/web/handler.clj",
    "chars": 478,
    "preview": "(ns afterglow.web.handler\n  (:require [afterglow.web.middleware :as middleware]\n            [afterglow.web.routes.home :"
  },
  {
    "path": "src/afterglow/web/layout.clj",
    "chars": 1175,
    "preview": "(ns afterglow.web.layout\n  (:require [clojure.java.io]\n            [environ.core :refer [env]]\n            [markdown.cor"
  },
  {
    "path": "src/afterglow/web/middleware.clj",
    "chars": 3029,
    "preview": "(ns afterglow.web.middleware\n  (:require [afterglow.web.layout :refer [*servlet-context* *identity*]]\n            [after"
  },
  {
    "path": "src/afterglow/web/routes/home.clj",
    "chars": 2147,
    "preview": "(ns afterglow.web.routes.home\n  (:require [afterglow.show :as show]\n            [afterglow.version :as version]\n        "
  },
  {
    "path": "src/afterglow/web/routes/show_control.clj",
    "chars": 46775,
    "preview": "(ns afterglow.web.routes.show-control\n  (:require [afterglow.controllers :as controllers]\n            [afterglow.control"
  },
  {
    "path": "src/afterglow/web/routes/visualizer.clj",
    "chars": 8262,
    "preview": "(ns afterglow.web.routes.visualizer\n  (:require [afterglow.effects.movement :as movement]\n            [afterglow.fixture"
  },
  {
    "path": "src/afterglow/web/routes/web_repl.clj",
    "chars": 4789,
    "preview": "(ns afterglow.web.routes.web-repl\n  \"Provides a web interface for interacting with the Clojure environment.\"\n  (:require"
  },
  {
    "path": "src/afterglow/web/session.clj",
    "chars": 1076,
    "preview": "(ns afterglow.web.session\n  \"Manages the session store for Afterglow's web interface\"\n  (:require [afterglow.web.routes."
  },
  {
    "path": "test/afterglow/core_test.clj",
    "chars": 194,
    "preview": "(ns afterglow.core-test\n  (:require [clojure.test :refer :all]\n            [afterglow.core :refer :all]))\n\n(deftest a-te"
  },
  {
    "path": "test/afterglow/effects/color_test.clj",
    "chars": 509,
    "preview": "(ns afterglow.effects.color-test\n  (:require [clojure.test :refer :all]\n            [afterglow.effects.color :refer :all"
  },
  {
    "path": "test/afterglow/effects_test.clj",
    "chars": 29236,
    "preview": "(ns afterglow.effects-test\n  (:require [clojure.test :refer :all]\n            [afterglow.effects :refer :all]\n          "
  }
]

// ... and 13 more files (download for full content)

About this extraction

This page contains the full source code of the brunchboy/afterglow GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 169 files (223.1 MB), approximately 873.1k tokens, and a symbol index with 203 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!