• This repository has been archived on 28/Apr/2023
  • Stars
    star
    1,563
  • Rank 29,940 (Top 0.6 %)
  • Language
    Common Lisp
  • License
    Other
  • Created about 3 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Generative art in Common Lisp

WEIRD-A Generative Art System

NOTE: I will probably not update this repo anymore. See auxin. For a stripped version of this repo

**NOTE: weird reqires cl-veq. the most recent compatible version of cl-veq must be installed locally for this system to work properly: https://github.com/inconvergent/cl-veq/releases/tag/v4.1.0-beta **

NOTE: An improved version of the graph data structure in the weir package can be found in my new project: cl-grph. Among multiple improvements, the graph structure in grph is immutable, and supports Datalog queries.

About

weird is the next iteration of weir, which was the next iteration of snek.

The library is written to be useful for a broad range of ways in which I create art using generative algorithms. Almost everything I have made over the past several years has been made using some version of this system.

Elastic Web

Components

Here are the main components:

  1. 2d/3d vector mathematics via cl-veq. See examples in veq for more details.

  2. A simple (undirected) graph data structure called weir. The structure can be manipulated directly, or via alterations. The latter is described in more detail below. Here is a simple example of how you can manipulate the structure directly:

    (in-package :weir)
    (let ((wer (make)))
      ; add three edges
      (loop repeat 3
            do (add-edge! wer
                 (2add-vert! wer
                   (rnd:2in-circ 200.0))
                 (2add-vert! wer
                   (veq:f2+ (veq:2rep 500.0)
                            (rnd:2in-circ 200.0))))
      ; iterate verts
      (itr-verts (wer v)
        ; prints vert coordinates
        (veq:vpr (2get-vert wer v)))
    
      ; iterate edges
      (itr-edges (wer vv)
        (veq:vpr (2get-verts wer vv)))
    
      ; move a vert relativ to current position:
      (2move-vert! wer 0 1.0 2.0)
      ; or to an absolute position
      (2move-vert! wer 1 1.0 2.0 :rel nil)
    
      ; edges are represented as lists of verts, and they are always
      ; sorted with the smallest vert index first, so both of these
      ; return t:
      (edge-exists wer '(0 1))
      (edge-exists wer '(1 0))
    
      ; get edges incident to vert 0
      (get-incident-edges wer 0))

    See examples/draw.lisp and examples/ex.lisp for more.

  3. Random numbers, some examples:

    (in-package :rnd)
    (rnd a) ; in range [0.0, a), defaults to a=1.0.
    (rnd* a) ; in range [-a, a), defaults to a=1.0.
    (rndrng a b) ; in range [a, b)
    (rndi 10) ; random fixnum
    (rndspace n a b) ; n numbers in [a, b)
    (norm :mu 0.0 :sigma 1.0) ; normal distribution
    (2in-circ a) ; in circle of radius a
    (2in-rect w h) ; in a rectangle
    (2nin-rect n w h) ; n in rectangle.
    (2on-line ax ay bx by) ; point between points a and b
    
    ; do something with probability 0.1, second form is optional
    (prob 0.1 (print "10% hi") (print "90% oh no"))
    
    ; perform either form 1 or (optionally) 2
    (either (print "form 1") (print "form 2"))

    See rnd.lisp, 2rnd.lisp and 3rnd.lisp, for all available functions.

  4. A tool for drawing svg files: wsvg. See draw.lisp.

In addition the library contains a number of useful tools for dealing with (predominantly) vector graphics.

Sun

Weir Graphs and Alterations

In my opinion, the most interesting part of the weir graph structure is alterations. An alteration is a change that will be applied to the structure at the end of a given context, provided it is valid.

The main motivation behind this is that this makes it possible to gather up a number of changes that will be applied to the graph at a later time. This makes it possible to access the state in the weir instance while you are creating the alterations. Without there being any changes made to the state of the weir instance while the alterations are being created. Once all alterations are created, the valid ones will be applied.

Existing alterations in weir are postfixed with ?. It might look like this:

(weir:with (wer %)
  (% (add-vert? (veq:f2 100.0 740.0))
  (% (add-edge? 1 4)))

(% ...) is used to collect alterations. They will be executed at the end of the with context. If an alteration evaluates to nil, nothing will happen.

Here is an example of how the forces are calculated in my Tangle of Webs simulation:

(veq:vdef* reciprocal-edge-forces (wer &key (stp 0.1))
  (weir:with (wer %)
    ; state of wer is unaltered
    (weir:itr-edges (wer e) ; edge (v0 v1)
      ; vector from v0 to v1
      ; force is proportional to this "oriented distance"
      (veq:f2let ((force (veq:f2-
                           (veq:f2$ (weir:2get-verts wer e)
                                    1 0))))
        (loop for i in e and s in '(-1.0 1.0)
              ; alteration is created, but nothing happens
              do (% (2move-vert? i
                      (veq:f2scale force (* s stp)))))))))
    ; alterations are applied at the end
    ; of the context

The important thing to note here is that for the forces to be calculated correctly, all edge lengths must be calculated before the forces are applied to the vertices.

Symbols

Futures and Dependencies

You can assign a name to the result of an alteration using

(% (add-edge? 1 3) :res :some-name?)

This makes it possible to create alterations that depend on the result of other alterations:

(in-package :weir)
(with (wer %)
  (veq:f2let ((pt (veq:f2 1f0 3f0)))
    (% (2add-vert? pt) :res :a?) ; alteration result is named :a?
    (% (2add-vert? (veq:f2 1.0 2.0)) :res :b?) ; result named :b?
    (% (add-edge? :a? :b?)))) ; depends on :a? and :b?

; all alteration results:
(print (get-alteration-result-list wer))
; or as a `hash-map`:
(print (get-alteration-result-map wer))

alteration names must be keywords that end with ?. (There is an exception, see Looping below.) And using the same name for multiple alterations will result in undefined behaviour.

As you can see, a named alteration is akin to a future; a reference to a result that may or may not exist eventually. For this to work, any alteration that depends on a future that fails (or returns nil) will be skipped.

You can use (weir:with (wer % :bd t) ...) to see how an alteration is expanded. This might make it easier to see what is going on.

As en example. The alteration:

(% (2move-vert? :vert?
     (veq:f2scale
       (veq:f2- (veq:f2$ (weir:2get-verts wer '(1 3)) 1 0))
       1f0)))

will be expanded to:

(VEQ:F2LET
 ((#:OUT-F2!P53
   (VEQ:F2SCALE (VEQ:F2- (VEQ:F2$ (WEIR:2GET-VERTS WER '(1 3)) 1 0)) 1.0)))
 (LET ((#:OUT-REL54 T))
   (LAMBDA (#:WER541)
     (CASE (WEIR::-IF-ALL-RESOLVED #:ALT-RES29 (LIST :VERT?))
       (:OK
        (VALUES T
                (PROGN
                 (WHEN
                     (WEIR::-VALID-VERT #:WER541
                                        (VALUES (GETHASH :VERT? #:ALT-RES29)))
                   (PROGN
                    (WEIR:2MOVE-VERT! #:WER541
                                      (VALUES (GETHASH :VERT? #:ALT-RES29))
                                      (WEIR::VAL* #:OUT-F2!P53)
                                      :REL #:OUT-REL54)
                    (VALUES (GETHASH :VERT? #:ALT-RES29)))))))
       (:BAIL (PROGN NIL (VALUES T NIL)))
       (T (VALUES NIL NIL))))))

Which won't work in its own unless :VERT? is also defined. But you can see how the promise resolution is handled. And how values (#:OUT-REL54, #:OUT-F2!P53) are defined in the surrounding closure.

Scribbles

Looping

It is possible to use alterations inside loops as well. but it requires a bit more careful consideration. Here is an example:

(in-package :weir)
(with (wer % :db t)
  (loop for x in (math:linspace 20 -20.0 20.0) do
    (loop for z in (list 1.0 2.0) do
      (veq:f3let ((xy (veq:f3 x y z)))
        ; create a distinct name
        (let ((g? (gensym "g")))
          (% (add-grp? :name (gensym "line")) :res g?)
          (% (2add-path?
               (veq:f$_ (list (veq:f3-
                                xy (veq:f3 1.0 8.0 (rnd:rnd)))
                              (veq:f3+
                                xy (veq:f3 1.0 2.0 (rnd:rnd)))))
               :g g?)))))))

Writing

I have written about things related to this code at:

Note that these posts refer to older iterations of the code. So some of the things will be out of date.

Boxes

On Use and Contributions

This code is written for my personal use, and parts of it is rather experimental. Also, it is likely to change at my whim. For this reason I don't recommend depending on this library for anything.

I release it publicly in case people find it useful or interesting. It is not, however, intended as a collaboration/Open Source project. As such I am unlikely to accept PRs, reply to issues, or take requests.

Installation and Dependencies

weird depends on cl-veq, and it requires Quicklisp to install other dependencies (which are listed in weird.asd).

To install and load weird, do:

(ql:quickload :weird)

If this does not work, weird may not be in a place Quicklisp or ASDF can see them. To fix this, either:

(load "weird.asd")

For a long term solution, add the following to .sbclrc:

#+quicklisp
(push "/path/to/dir/containing/weird" ql:*local-project-directories*)

You will have to make sure cl-veq is also available in the same fashion for any of this to work.

Versions and Compatability

Weird version 6.1.0 requires version cl-veq 2.2.0.

Tests

Tests can be executed using: (asdf:test-system :weird).

Thanks

I would like to thank:

Who have provided me with useful hints and code feedback.

The ASDF config and test setup was kindly suggested and implemented by Robert Smith (https://twitter.com/stylewarning). Although I have made some changes since then.

Also, many thanks to https://twitter.com/xach for making Quicklisp.

More Repositories

1

snek

See https://github.com/inconvergent/weir instead
Common Lisp
733
star
2

differential-line

a generative algorithm
Python
674
star
3

weir

(deprecated) A system for making generative systems
Common Lisp
633
star
4

sand-spline

generative algorithm
Python
299
star
5

svgsort

svg path sorter for more efficient (pen)plotting.
Python
241
star
6

tree

An algorithm that draws procedurally generated trees.
Python
183
star
7

fracture

generative algorithm
Python
119
star
8

inconvergent-sandbox

Interactive demos written in p5.js
JavaScript
115
star
9

hyphae

Python
97
star
10

gridfont

grid-based system for designing simple symbols and fonts. including an example font. mainly intended for plotter drawings.
Python
95
star
11

fn

python library for generating file names based on current time and git commit.
Python
89
star
12

differential-mesh

generative algorithm
Python
84
star
13

differential-lattice

a generative algorithm using CUDA
Python
73
star
14

desert

A fast (?) random sampling drawing library
Python
65
star
15

differential-mesh-3d

a generative algorithm
Python
55
star
16

hyphae_ani

generative algorithm
Python
49
star
17

orbitals

A generative algorithm. See inconvergent/orbitals_speedup
Python
48
star
18

sand-glyphs

generative algorithm
Python
46
star
19

cl-grph

In-Memory Immutable Graph Structure with Datalog Query Language in Common Lisp
Common Lisp
45
star
20

linearx-font

Procedurally generated fonts
Python
44
star
21

auxin

Auxiliary Common Lisp Utilities for Generative Art
Common Lisp
43
star
22

cl-veq

Vector mathematics DSL and utilities in Common Lisp
Common Lisp
41
star
23

boids

More or less an implementation of Boids flocking algorithm.
Python
36
star
24

orbitals_speedup

Python
36
star
25

simple-circle-packing

brief intro to python + cairo for visualization/drawing
Python
33
star
26

leaf-cuda

leaf venation algorithm. work in progress
Python
28
star
27

axidraw-xy

WARNING: THIS IS OUTDATED AND UNMAINTAINED.
Python
27
star
28

fast-sand-paint

fast wrapper around some pixel manipulation
Python
26
star
29

tracepath-spline

A generative algorithm that draws wave-like lines.
Python
24
star
30

fracture-cuda

Python
22
star
31

differential-line-cuda

CUDA implementation of Differential Line
Python
20
star
32

svg-sorter

spatially sorts all lines in a svg, and creates new svg that is reasonably efficient for plotting.
Python
17
star
33

render

[unmaintained] helpers used to draw and animate with python (cairo)
Python
14
star
34

talks

Notes from presentations I've given.
Python
14
star
35

iutils

[DEPRECATED] utility functions for several generative algorithms.
Python
11
star
36

wind

Python
10
star
37

ddd-utils

[unmaintained] helpers used in various generative algorithm pieces (random, vectors, import/export)
Python
9
star
38

zonemap

unmaintained
Python
8
star
39

automata

unmaintained
Python
7
star
40

differential_ani

[outdated, see https://github.com/inconvergent/differential-line]
Python
7
star
41

differential-cloud

Work in progress.
Python
7
star
42

leaf

Python
6
star
43

xmsmfem

A parallel Multiscale Mixed Finite Element Method for the Matlab Reservoir Toolbox using the Matlab Parallel Computing Toolbox
MATLAB
6
star
44

annotate

adhoc system for annotating image quality and storing the result in a csv
Python
5
star
45

orbitals_ani

A generative algorithm.
Python
5
star
46

sand-pillars

unmaintained
Python
5
star
47

differential-line-mpi

Python
5
star
48

brush-test

JavaScript
4
star
49

sand-dunes

unmaintained
Python
4
star
50

inconvergent

4
star
51

zet

Python
3
star
52

ccvt

Python
3
star
53

zonemap-3d

unmaintained
Python
2
star
54

kinect-glitter

JavaScript
2
star
55

win-vin

2
star
56

jqn

JSON query and transform terminal utility and Common Lisp library
Common Lisp
2
star
57

stacker

Python
1
star
58

orbitals_multicanvas

Python
1
star
59

stm

Finite State Machine Utilities
Common Lisp
1
star