{"id":13442603,"url":"https://github.com/inconvergent/weir","last_synced_at":"2025-03-20T14:31:39.344Z","repository":{"id":52661779,"uuid":"254811865","full_name":"inconvergent/weir","owner":"inconvergent","description":"(deprecated) A system for making generative systems","archived":true,"fork":false,"pushed_at":"2021-04-21T21:18:38.000Z","size":12270,"stargazers_count":637,"open_issues_count":0,"forks_count":31,"subscribers_count":19,"default_branch":"master","last_synced_at":"2024-10-28T05:59:26.240Z","etag":null,"topics":["art","common-lisp","generative","generative-art","graph","image","plotting","sbcl"],"latest_commit_sha":null,"homepage":"https://inconvergent.net","language":"Common Lisp","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/inconvergent.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-04-11T07:01:31.000Z","updated_at":"2024-09-21T12:29:47.000Z","dependencies_parsed_at":"2022-08-21T14:00:57.319Z","dependency_job_id":null,"html_url":"https://github.com/inconvergent/weir","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inconvergent%2Fweir","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inconvergent%2Fweir/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inconvergent%2Fweir/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inconvergent%2Fweir/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/inconvergent","download_url":"https://codeload.github.com/inconvergent/weir/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244630207,"owners_count":20484336,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["art","common-lisp","generative","generative-art","graph","image","plotting","sbcl"],"created_at":"2024-07-31T03:01:47.890Z","updated_at":"2025-03-20T14:31:37.248Z","avatar_url":"https://github.com/inconvergent.png","language":"Common Lisp","readme":"# WEIR-A System for Making Generative Systems\n\n\n## About\n\nThis library is specifically written to be useful for a broad range of ways in\nwhich I create art using various generative algorithms. It is the next\niteration of [snek](https://github.com/inconvergent/snek). I made a new version\nbecause I wanted to make some significant changes.\n\nMain components:\n\n1. 2d and 3d vectors with corresponding operations:\n   ```lisp\n   ; create a 2d vector\n   (vec:vec 1d0 3d0)\n\n   ; create a 3d vector\n   (vec:3vec 1d0 2d0 3d0)\n   ```\n   `vec` supports common vector operations such as `add`, `mult`, `div`, `sub`,\n   as well as cross products `cross`, dot products, `dot`.\n\n   *Note: Functions that operate on 3d vectors are prefixed with `3`.*\n\n   Furthermore there are corresponding functions for scalars, lists of vectors,\n   and broadcasting. They are indicated by prefix `l` and `s`:\n   ```lisp\n   ; add scalar s to a, return result\n   (vec:sadd va s)\n\n   ; add vectors in two lists, returns list of new vectors\n   (vec:ladd aa bb)\n\n   ; add b to elements of aa, return list of new vectors\n   (vec:ladd* aa vb)\n   ```\n\n   Most of these functions also have a corresponding function postfixed with `!`,\n   which performs the same operation, but assigns the result to the first\n   argument. This is faster since there is no need to create a new `vec` struct.\n\n   *Note: This can cause strange behaviour since you can inadvertently change the\n   internal state of a struct. Use with caution.*\n\n   Example:\n   ```lisp\n   ; add b to a, store result in a; also returns a\n   (vec:add! va vb)\n   ```\n   There are also some common geometric operations, such as line interpolation,\n   line-line intersection, angles, and line-plane intersection.\n   ```lisp\n   ; find the position s between a and b. s should be between 0 and 1\n   (vec:on-line s va vb)\n   ; find intersection of two lines, returns\n   ; a bool, and interpolation value along line 1, line 2\n   (vec:segx line1 line2)\n   ```\n   See the code in the package for more details.\n\n2. Random numbers, some examples:\n   ```lisp\n   ; random double 0d0 and a (default: 1d0)\n   (rnd:rnd a)\n\n   ; ditto, between -a and a\n   (rnd:rnd* a)\n\n   ; between a and b\n   (rnd:rndrng a b)\n\n   ; random fixnum\n   (rnd:rndi 10)\n\n   ; return n random numbers between a and b\n   (rnd:rndspace n a b)\n\n   ; random number from normal distribution\n   (rnd:norm :mu 0d0 :sigma 1d0)\n\n   ; uniform in circle of radius a at centered at p\n   (rnd:in-circ a :xy p)\n\n   ; uniform numbers in a rectangle\n   (rnd:in-rect w h :xy p)\n\n   ; pick a random point between points va and vb\n   (rnd:on-line va vb)\n\n   ; execute a form with a certain probability\n   ; second form is optional\n   (rnd:prob 0.1d0 (print \"10% hi\") (print \"90% oh no\"))\n\n   ; perform either form 1 or 2\n   (rnd:either (print \"form 1\") (print \"form 2\"))\n\n   ; repeat the form at most n-1 times\n   (rnd:rep n (print 1))\n   ```\n   There are also some utils for random 3d vector numbers, see\n   `src/rnd/3rnd.lisp`.\n\n3. A simple graph data structure, `weir`, for working with vertices and edges.\n   The structure is combined with a programming pattern for applying changes to\n   the structure. The pattern relies on `alterations`, see below. You can also\n   manipulate the structure directly.\n\n   Below is a small example:\n   ```lisp\n   (let ((wer (weir:make))) ; use :dim 3 for 3d\n     ; add three edges\n     (loop repeat 3\n           do (weir:add-edge! wer\n                (weir:add-vert! wer (rnd:in-circ 200d0))\n                (weir:add-vert! wer (rnd:in-circ 200d0\n                                :xy (vec:rep 500d0)))))\n     ; iterate verts\n     (weir:itr-verts (wer v) (print (weir:get-vert wer v)))\n\n     ; move a vert relativ to current position:\n     (weir:move-vert! wer 0 (vec:vec 1d0 2d0))\n     ; move a vert to an absolute position\n     (weir:move-vert! wer 1 (vec:vec 1d0 2d0) :rel nil)\n\n     ; iterate edges\n     (weir:itr-edges (wer vv) (print (weir:get-verts wer vv)))\n\n     ; edges are represented as lists of verts, and they are always\n     ; sorted with the smallest vert index first, so both\n     (weir:edge-exists wer '(0 1)) ; and\n     (weir:edge-exists wer '(1 0)) ; returns t\n\n     ; get edges incident to vert 0\n     (weir:get-incident-edges wer 0))\n   ```\n   See the `examples` folder for more.\n\n4. A series of other useful data structures and tools. E.g. a package for\n   handling colors (`pigment`), splines (`bzspl`), and various vector and path\n   functionality. Eg. `math`, `lin-path` and `simplify-path`.\n   ```lisp\n   (let* ((points (rnd:nin-circ 5 400d0))\n          (bz (bzspl:make points))\n          (lp (lin-path:make points)))\n     ; sample a point on the spline\n     (bzspl:pos bz (rnd:rnd))\n     ; sample a point on path\n     (lin-path:pos lp (rnd:rnd))\n\n     ; represent the spline with a limited number of points\n     (bzspl:adaptive-pos bz :lim 1d0))\n\n   ; return n numbers evenly spaced between a and b, inclusive\n   (math:linspace n a b :end t)\n\n   ; all fixnums from a to b-1\n   (math:range a b)\n\n   ; repeat the form n times\n   (math:nrep n (rnd:rnd))\n   ```\n\n5. Orthogonal projection `ortho`:\n   ```lisp\n   (let ((proj (ortho:make :s 1d0\n                           :xy (vec:rep 500d0)\n                           :cam (vec:3vec 1000d0 1000d0 0d0)\n                           :look (vec:3zero))))\n     (multiple-value-bind (v d)\n       (ortho:project proj (rnd:3in-sphere :rad 300d0))\n       ; point in 2d\n       (print v)\n       ; distance from 3d point to camera plane\n       (print d))\n\n     ; update cam position and look at something else\n     (ortho:update proj :cam (vec:3vec 3d0 4d0 1d0)\n                        :look (vec:3rep 79d0)))\n   ```\n\n6. A tool for drawing `svg` files: `draw-svg`. Mainly files that are good for\n   plotting.\n   ```lisp\n   (let ((psvg (draw-svg:make :stroke \"black\"))\n         (pts (list (vec:vec 10d0 20d0) (vec:vec 20d0 30d0)\n                    (vec:vec 10d0 50d0))))\n     ; sw is the stroke width\n     (draw-svg:path psvg pts :sw 3d0)\n     (draw-svg:bzspl psvg pts :sw 3d0 :so 0.5d0)\n     (draw-svg:circ psvg (vec:rep 30d0) 10d0 :sw 3d0 :stroke \"red\")\n     (draw-svg:save psvg \"filename\"))\n   ```\n\n7. A tool for drawing `pngs` called `sandpaint`. This package uses random\n   sampling to draw. This creates a fairly distinct and gritty look in many\n   cases. Also supports direct pixel manipulations and a few filters.\n\n\n## Weir Graphs and Alterations\n\nAn `alteration` is a change that will be applied to the structure at the end of\na given context. In practical terms, an alteration is a function that returns a\n`lambda` (or just a `lambda`).\n\nThe main motivation behid this is that this makes it possible to \"queue\" up a\nnumber of changes that will be applied at a later time. This makes it possible\nto access the state in the `weir` instance while you are creating the\nalterations. Without there being any changes made to the state of the `weir`\ninstance while the alterations are being created. Once all alterations are\ncreated, they will be applied.\n\nExisting alterations in `weir` are postfixed `?` by convention, and it might\nlook like this:\n\n```lisp\n(weir:with (wer %)\n  ( ; some code\n    (% (weir:add-vert? ...))\n    ; more code\n    (% (weir:add-edge? ...))))\n```\n\nall `(% ...)` forms inside the weir context will cause the alteration inside to\nbe created and collected. They will be executed at the end of the context. if\nan alteration evaluates to nil, nothing will happen.\n\n### Names and Args\n\nYou can assign a name (`:res`) to the result of an alteration. This makes it\npossible to create alterations that depend (`:arg`) on the result of other\nalterations:\n\n```lisp\n(weir:with (wer %)\n  (let ((pt (...)))\n    (% (weir:add-vert? pt) :res :a) ; alteration result is named :a\n    (% (weir:add-vert? (vec:vec 1d0 2d0)) :res 'b) ; result is named 'b\n    (% (weir:add-edge? :a 'b) :arg (:a 'b)))) ; uses :a and 'b\n```\n\nNote that `res` must be a keyword, symbol, or a variable with keyword or symbol\nvalue. Similarly, `arg` must be a list of `res` elements that exist inside the\nsame context. make sure that all elements in the `:arg` are present in the\ncontext, or the code will loop infinitely.\n\nit is always possible to both reference future results, and assign the result a\nname. The order the order of `:res` and `:arg` does not matter:\n\n```lisp\n(% (some-alteration? :a :b) :res :x :arg (:a :b)) ; is equivalent to\n(% (some-alteration? :a :b) :arg (:a :b) :res :x)\n```\n\nResults will be available after the `(with:weir ...)` context. See\n`(get-alteration-result-list)` or `(get-alteration-result-map)`.  Also, note\nthat using the same name for multiple alterations _will_ result in undefined\nbehaviour.\n\n### Dependency and Futures\n\nYou can consider a named alteration as something akin to a _future_; the value\nof `:res` is a reference to a value that does not yet exist. For this to work,\nany alteration that depends on a future that fails to be fulfilled will be\nskipped.\n\nAs an example, we can make the alteration `prob-add-edge?` like this:\n\n```lisp\n(defun prob-add-edge? (l a b)\n  'add edge (a b) with probability p'\n  (lambda (w) (when (\u003c (rnd:rnd 100d0) l)\n                    (add-edge! w a b))))\n```\n\nThis will attempt to create edge `(a b)`, but only if the random number is less\nthan `l`.  This is to illustrate that the alteration may or may not attempt to\n`weir` instance.  If no edge is created, the above lambda will return `nil`.\n\nHere is a an example of use:\n\n```lisp\n; context start\n(let (wer (weir:make))\n\n  ; add some data to wer here ...\n\n  ; (% ...) is used to accumulate alterations\n  ; alterations are applied at the end of (weir:with ...)\n  (weir:with (wer %)\n    ; iterate all vertices in wer\n    (weir:itr-verts (wer v)\n      (% (move-vert? v (rnd:in-circ 10d0)))\n      ; w will be an arbitrary vertex in wer\n      (weir:with-rnd-vert (wer w)\n        (% (prob-add-edge? (weir:edge-length wer v w) v w))))))\n```\n\nThe important thing to note here is that it is _crucial_ that the\nlength of edge `(v w)` is calculated outside `(defun prob-add-edge? ...)`.\nThis ensure that `(weir:edge-length wer v w)` is the length of the edge\n_before_ the calls to `(move-vert? ...)` have a chance to move either `v` or\n`w`.\n\nNaturally, you can construct an alteration that checks the length of the edge\ninside the  `lambda` in `(prob-add-edge? ...)`, but this will result in\ndifferent behaviour. In this case, any edge length will be calculated while\nall the other vertices are moving around. Thus resulting in what can be\nconsidered \"unexpected side effects\".\n\nThis becomes more clear if you consider an n-body simulation. If any single\nbody in the simulation moves before you have calculated the force between each\npair of bodies, you will get an incorrect result.\n\n### Shadowing\n\nAs we have mentioned, arguments to an alteration may, or may not, exist right\naway.  To handle this, arguments to an alteration will be shadowed before the\nalteration is collected. This applies to arguments that are atoms, or forms\nthat do not contain a reference to a future alteration result. This is the\nbehaviour you will usually want in an example such as the one above. But it\nmight cause unexpected behaviour.\n\nAs an example of what happes, consider the alteration:\n\n```lisp\n(% (my-alteration? (first var-1)\n                   :a\n                   (my-function (rnd:rnd) :b (second var-2)))\n   :arg (:a :b))\n```\n\nThis will be expanded by the macro to something similar to:\n\n```lisp\n(LET ((#:|non-atom:91| (FIRST VAR-1)) ; values are evaluated at time of collection\n      (#:|non-atom:92| (RND:RND))\n      (#:|non-atom:93| (SECOND VAR-2)))\n  (LAMBDA (#:WNAME90)\n    (CASE (WEIR::-IF-ALL-RESOLVED #:ALT-RES88 (LIST :A :B))\n      (:OK ; :A and :B both have a value\n       (VALUES T\n               (FUNCALL\n                (THE FUNCTION\n                     (MY-ALTERATION? #:|non-atom:91| (GETHASH :A #:ALT-RES88)\n                      (MY-FUNCTION #:|non-atom:92| (GETHASH :B #:ALT-RES88)\n                                     #:|non-atom:93|)))\n                #:WNAME90)))\n      (:BAIL (VALUES T NIL)) ; either :A or :B returned nil. skip alteration\n      (:WAIT (VALUES NIL NIL))))) ; :A or :B does not yet exist\n```\n\nAs you can see, the variables/forms that will be shadowed here are: `(first var-1)`,\n`(second var-2)` and `(rnd:rnd)`.\n\nYou can use `(weir:with ... :bd t)` to see how an alteration is expanded. This\nmight make it easier to find issues with shadowed/non-shadowed variables. Also,\nyou can usually solve some problems you might encounter by defining custom\nalterations locally (but outside `weir:with`) using `(labels ())`.\n\n### Looping\n\nIt is possible to use `:ref` and `:arg` inside loops as well. but it requires\na bit more careful consideration. Here is an example:\n\n```lisp\n(weir:with (wer % :db t)\n  (loop for x in (math:linspace 20 -20d0 20d0)\n        do (loop for z in (list 1d0 2d0)\n                 do (let ((xy (vec:vec x y z))\n                          (s (vec:vec 1d0 80d0))\n                          (g (gensym \"g\"))) ; create a distinct name\n                      (% (weir:add-grp? :name (gensym \"line\")) :res g)\n                      (% (weir:add-path? (list (vec:sub xy s) (vec:add xy s))\n                                         :g g)\n                         :arg (g))))))\n```\n\nThe second alteration will be expanded to:\n\n```lisp\n(LET ((#:|non-atom:8| (LIST (VEC:SUB XY S) (VEC:ADD XY S))))\n  (LAMBDA (#:WNAME7)\n    ; every G is now a distinct future\n    (CASE (WEIR::-IF-ALL-RESOLVED #:ALT-RES3 (LIST G))\n      (:OK\n       (VALUES T\n               (FUNCALL\n                (THE FUNCTION\n                     (WEIR:ADD-PATH? #:|non-atom:8| :G\n                                     (GETHASH G #:ALT-RES3)))\n                #:WNAME7)))\n      (:BAIL (VALUES T NIL))\n      (:WAIT (VALUES NIL NIL)))))\n\n```\n\n### Custom Alterations\n\nYou can define your own arbitrary alterations. There is an example of\ncustom alterations and references in `examples/custom-alt.lisp`.\n\n### Final Note\n\nIn the previous implementations of the `(weir:with ...)` context, `(% ...)` was\na function. This ensured that the arguments to the alteration, and indeed the\nlambda inside the alteration, was created before the end of the context. This\neradicated the need for these complex shadowing rules. I'm not currently sure\nwhether it is possible to avoid shadowing as long as some arguments do not\nexist at the time the alteration is collected.\n\nAlso, I use the term \"shadowing\" above. Not sure if this is really appropriate,\nbut I failed to think of a better term\n\n\n## Use\n\nI use `weir` for most of the work that I post online\n(https://inconvergent.net/, https://img.inconvergent.net/,\nhttps://twitter.com/inconvergent). Both for raster images, as well as vector\nimages for plotter drawings.\n\nHere are some plotted examples:\n\n  - https://inconvergent.net/2017/spline-script-plots/\n  - https://inconvergent.net/mechanical-plotter-drawings/\n  - https://inconvergent.net/mechanical-plotter-drawings/3/\n  - https://inconvergent.net/mechanical-plotter-drawings/5/\n\n\n## Writing\n\nI have written about things related to this code (when it was called `snek`) at:\n\n  - https://inconvergent.net/2017/snek-is-not-an-acronym/\n  - https://inconvergent.net/2017/a-method-for-mistakes/\n  - https://inconvergent.net/2017/arbitrary-alterations/\n  - https://inconvergent.net/2017/grains-of-sand/\n  - https://inconvergent.net/2017/a-propensity-for-mistakes/\n\nAnd recently at:\n\n  - https://inconvergent.net/2020/future-alterations/\n\n\n## On Use and Contributions\n\nThis code is written for my personal use, and parts of it is rather\nexperimental. Also, it is likely to change at my whim. For this reason I don't\nrecommend depending on this library for anything.\n\nI release it publicly in case people find it useful or interesting. It is not,\nhowever, intended as a collaboration/Open Source project. As such I am unlikely\nto accept PRs, reply to issues, or take requests.\n\n\n## Installation and Dependencies\n\nThis code requires Quicklisp to install dependencies (which are listed in\n`weir.asd`). To install and load Weir, do:\n```lisp\n(ql:quickload :weir)\n```\nIf this does not work, Weir may not be in a place Quicklisp or ASDF can see\nthem. To fix this, either\n```lisp\n(load \"weir.asd\")\n```\nor, for a long term solution, push the directory in which Weir sits to the\nvariable `quicklisp:*local-project-directories*`:\n```lisp\n; in your .sbclrc, for example:\n#+quicklisp\n(push \"/path/to/dir/containing/weir\" ql:*local-project-directories*)\n```\n\nThe `fn` package (for generating file names) depends on the `fn` command from\nhttps://github.com/inconvergent/fn, but this is not necessary to use any of the\nother packages.\n\nThe code has only been tested in `Ubuntu 18.04 LTS` with `SBCL 2.0.1`. I've\nbeen told that examples work with `SBCL` in `macOS`.\n\n\n## Tests\n\nRun:\n```lisp\n(asdf:test-system :weir)\n```\n\n\n## In Case of QL Version Issues\n\nSee http://blog.quicklisp.org/2011/08/going-back-in-dist-time.html\n\nSummary:\n```lisp\n(use-package :ql-dist)\n; see versions\n(available-versions (dist \"quicklisp\"))\n; select a dist version\n(install-dist\n  \"http://beta.quicklisp.org/dist/quicklisp/2019-03-07/distinfo.txt\"\n  :replace t)\n```\n\n\n## Thanks\n\nI would like to thank:\n\n  - https://twitter.com/RainerJoswig\n  - https://twitter.com/jackrusher\n  - https://twitter.com/paulg\n  - https://twitter.com/porglezomp\n  - https://twitter.com/stylewarning\n  - https://github.com/Hellseher\n\nWho have provided me with useful hints and code feedback.\n\nThe ASDF config and test setup was kindly suggested and implemented by Robert\nSmith (https://twitter.com/stylewarning). The remaining weirdness in the test\nsystem is my fault. Hope to fix it properly later.\n\nAlso, many thanks to https://twitter.com/xach for making Quicklisp.\n\n","funding_links":[],"categories":["Common Lisp","Applications"],"sub_categories":["Graphics"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finconvergent%2Fweir","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finconvergent%2Fweir","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finconvergent%2Fweir/lists"}