{"id":13629342,"url":"https://github.com/inconvergent/weird","last_synced_at":"2025-09-28T23:30:21.468Z","repository":{"id":41115642,"uuid":"437254922","full_name":"inconvergent/weird","owner":"inconvergent","description":"Generative art in Common Lisp","archived":true,"fork":false,"pushed_at":"2023-04-28T10:48:25.000Z","size":3100,"stargazers_count":1564,"open_issues_count":0,"forks_count":60,"subscribers_count":30,"default_branch":"master","last_synced_at":"2024-09-26T03:40:56.422Z","etag":null,"topics":["art","common-lisp","generative","generative-art","graph","graph-algorithms","lisp","plotter-art","plotters","svg","vector-graphics"],"latest_commit_sha":null,"homepage":"https://inconvergent.net","language":"Common Lisp","has_issues":true,"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,"governance":null}},"created_at":"2021-12-11T10:47:15.000Z","updated_at":"2024-09-22T21:59:30.000Z","dependencies_parsed_at":"2023-02-10T17:15:17.979Z","dependency_job_id":"23bc66a0-bc78-4d02-905f-756221331d73","html_url":"https://github.com/inconvergent/weird","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inconvergent%2Fweird","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inconvergent%2Fweird/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inconvergent%2Fweird/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inconvergent%2Fweird/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/inconvergent","download_url":"https://codeload.github.com/inconvergent/weird/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234569707,"owners_count":18854133,"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","graph-algorithms","lisp","plotter-art","plotters","svg","vector-graphics"],"created_at":"2024-08-01T22:01:07.989Z","updated_at":"2025-09-28T23:30:20.824Z","avatar_url":"https://github.com/inconvergent.png","language":"Common Lisp","readme":"# WEIRD-A Generative Art System\n\n**NOTE: I will probably not update this repo anymore. See\n[auxin](https://github.com/inconvergent/auxin). For a stripped version of this\nrepo**\n\n**NOTE: weird reqires cl-veq. the most recent compatible version of cl-veq must\nbe installed locally for this system to work properly:\nhttps://github.com/inconvergent/cl-veq/releases/tag/v4.1.0-beta **\n\n**NOTE: An improved version of the graph data structure in the `weir` package\ncan be found in my new project:\n[cl-grph](https://github.com/inconvergent/cl-grph). Among multiple\nimprovements, the graph structure in `grph` is immutable, and supports Datalog\nqueries.**\n\n## About\n\n`weird` is the next iteration of [weir](https://github.com/inconvergent/weir),\nwhich was the next iteration of [snek](https://github.com/inconvergent/snek).\n\nThe library is written to be useful for a broad range of ways in which I create\nart using generative algorithms. Almost everything I have made over the past\nseveral years has been made using some version of this system.\n\n![Elastic Web](img/web.png)\n\n## Components\n\nHere are the main components:\n\n1. 2d/3d vector mathematics via\n   [cl-veq](https://github.com/inconvergent/cl-veq).  See\n   [examples](https://github.com/inconvergent/cl-veq/blob/master/examples/ex.lisp)\n   in veq for more details.\n\n2. A simple (undirected) graph data structure called `weir`. The structure can\n   be manipulated directly, or via `alterations`. The latter is described in\n   more detail below. Here is a simple example of how you can manipulate the\n   structure directly:\n\n   ```lisp\n   (in-package :weir)\n   (let ((wer (make)))\n     ; add three edges\n     (loop repeat 3\n           do (add-edge! wer\n                (2add-vert! wer\n                  (rnd:2in-circ 200.0))\n                (2add-vert! wer\n                  (veq:f2+ (veq:2rep 500.0)\n                           (rnd:2in-circ 200.0))))\n     ; iterate verts\n     (itr-verts (wer v)\n       ; prints vert coordinates\n       (veq:vpr (2get-vert wer v)))\n\n     ; iterate edges\n     (itr-edges (wer vv)\n       (veq:vpr (2get-verts wer vv)))\n\n     ; move a vert relativ to current position:\n     (2move-vert! wer 0 1.0 2.0)\n     ; or to an absolute position\n     (2move-vert! wer 1 1.0 2.0 :rel nil)\n\n     ; edges are represented as lists of verts, and they are always\n     ; sorted with the smallest vert index first, so both of these\n     ; return t:\n     (edge-exists wer '(0 1))\n     (edge-exists wer '(1 0))\n\n     ; get edges incident to vert 0\n     (get-incident-edges wer 0))\n   ```\n   See [examples/draw.lisp](examples/draw.lisp) and\n   [examples/ex.lisp](examples/ex.lisp) for more.\n\n3. Random numbers, some examples:\n\n   ```lisp\n   (in-package :rnd)\n   (rnd a) ; in range [0.0, a), defaults to a=1.0.\n   (rnd* a) ; in range [-a, a), defaults to a=1.0.\n   (rndrng a b) ; in range [a, b)\n   (rndi 10) ; random fixnum\n   (rndspace n a b) ; n numbers in [a, b)\n   (norm :mu 0.0 :sigma 1.0) ; normal distribution\n   (2in-circ a) ; in circle of radius a\n   (2in-rect w h) ; in a rectangle\n   (2nin-rect n w h) ; n in rectangle.\n   (2on-line ax ay bx by) ; point between points a and b\n\n   ; do something with probability 0.1, second form is optional\n   (prob 0.1 (print \"10% hi\") (print \"90% oh no\"))\n\n   ; perform either form 1 or (optionally) 2\n   (either (print \"form 1\") (print \"form 2\"))\n   ```\n\n   See [rnd.lisp](src/rnd/rnd.lisp), [2rnd.lisp](src/rnd/2rnd.lisp) and\n   [3rnd.lisp](src/rnd/3rnd.lisp), for all available functions.\n\n4. A tool for drawing `svg` files: `wsvg`. See [draw.lisp](/examples/draw.lisp).\n\nIn addition the library contains a number of useful tools for dealing with\n(predominantly) vector graphics.\n\n![Sun](img/sun.png)\n\n## Weir Graphs and Alterations\n\nIn my opinion, the most interesting part of the `weir` graph structure is\n`alterations`. An `alteration` is a change that will be applied to the\nstructure at the end of a given context, provided it is valid.\n\nThe main motivation behind this is that this makes it possible to gather up a\nnumber of changes that will be applied to the graph at a later time. This makes\nit possible to access the state in the `weir` instance while you are creating\nthe alterations. Without there being any changes made to the state of the\n`weir` instance while the alterations are being created. Once all alterations\nare created, the valid ones will be applied.\n\nExisting alterations in `weir` are postfixed with `?`. It might look like this:\n\n```lisp\n(weir:with (wer %)\n  (% (add-vert? (veq:f2 100.0 740.0))\n  (% (add-edge? 1 4)))\n```\n\n`(% ...)` is used to collect alterations. They will be executed at the end of\nthe `with` context. If an `alteration` evaluates to `nil`, nothing will happen.\n\nHere is an example of how the forces are calculated in my [Tangle of Webs\nsimulation](https://inconvergent.net/2019/a-tangle-of-webs/):\n\n```lisp\n(veq:vdef* reciprocal-edge-forces (wer \u0026key (stp 0.1))\n  (weir:with (wer %)\n    ; state of wer is unaltered\n    (weir:itr-edges (wer e) ; edge (v0 v1)\n      ; vector from v0 to v1\n      ; force is proportional to this \"oriented distance\"\n      (veq:f2let ((force (veq:f2-\n                           (veq:f2$ (weir:2get-verts wer e)\n                                    1 0))))\n        (loop for i in e and s in '(-1.0 1.0)\n              ; alteration is created, but nothing happens\n              do (% (2move-vert? i\n                      (veq:f2scale force (* s stp)))))))))\n    ; alterations are applied at the end\n    ; of the context\n```\n\nThe important thing to note here is that for the forces to be calculated\ncorrectly, all edge lengths must be calculated _before_ the forces are applied\nto the vertices.\n\n![Symbols](img/symbols.png)\n\n### Futures and Dependencies\n\nYou can assign a name to the result of an alteration using\n```lisp\n(% (add-edge? 1 3) :res :some-name?)\n```\nThis makes it possible to create alterations that depend on the result of other\nalterations:\n\n```lisp\n(in-package :weir)\n(with (wer %)\n  (veq:f2let ((pt (veq:f2 1f0 3f0)))\n    (% (2add-vert? pt) :res :a?) ; alteration result is named :a?\n    (% (2add-vert? (veq:f2 1.0 2.0)) :res :b?) ; result named :b?\n    (% (add-edge? :a? :b?)))) ; depends on :a? and :b?\n\n; all alteration results:\n(print (get-alteration-result-list wer))\n; or as a `hash-map`:\n(print (get-alteration-result-map wer))\n```\n\n`alteration` names must be `keywords` that end with `?`. (There is an\nexception, see [Looping](#Looping) below.) And using the same name for multiple\nalterations _will_ result in undefined behaviour.\n\nAs you can see, a named alteration is akin to a _future_; a reference to a\nresult that may or may not exist eventually. For this to work, any alteration\nthat depends on a future that fails (or returns `nil`) will be skipped.\n\nYou can use `(weir:with (wer % :bd t) ...)` to see how an alteration is\nexpanded. This might make it easier to see what is going on.\n\nAs en example. The `alteration`:\n```lisp\n(% (2move-vert? :vert?\n     (veq:f2scale\n       (veq:f2- (veq:f2$ (weir:2get-verts wer '(1 3)) 1 0))\n       1f0)))\n```\nwill be expanded to:\n```lisp\n(VEQ:F2LET\n ((#:OUT-F2!P53\n   (VEQ:F2SCALE (VEQ:F2- (VEQ:F2$ (WEIR:2GET-VERTS WER '(1 3)) 1 0)) 1.0)))\n (LET ((#:OUT-REL54 T))\n   (LAMBDA (#:WER541)\n     (CASE (WEIR::-IF-ALL-RESOLVED #:ALT-RES29 (LIST :VERT?))\n       (:OK\n        (VALUES T\n                (PROGN\n                 (WHEN\n                     (WEIR::-VALID-VERT #:WER541\n                                        (VALUES (GETHASH :VERT? #:ALT-RES29)))\n                   (PROGN\n                    (WEIR:2MOVE-VERT! #:WER541\n                                      (VALUES (GETHASH :VERT? #:ALT-RES29))\n                                      (WEIR::VAL* #:OUT-F2!P53)\n                                      :REL #:OUT-REL54)\n                    (VALUES (GETHASH :VERT? #:ALT-RES29)))))))\n       (:BAIL (PROGN NIL (VALUES T NIL)))\n       (T (VALUES NIL NIL))))))\n```\nWhich won't work in its own unless `:VERT?` is also defined. But you can see how\nthe promise resolution is handled. And how values (`#:OUT-REL54`,\n`#:OUT-F2!P53`) are defined in the surrounding closure.\n\n![Scribbles](img/scribble.png)\n\n### Looping\n\nIt is possible to use `alterations` inside loops as well. but it requires a bit\nmore careful consideration. Here is an example:\n\n\n```lisp\n(in-package :weir)\n(with (wer % :db t)\n  (loop for x in (math:linspace 20 -20.0 20.0) do\n    (loop for z in (list 1.0 2.0) do\n      (veq:f3let ((xy (veq:f3 x y z)))\n        ; create a distinct name\n        (let ((g? (gensym \"g\")))\n          (% (add-grp? :name (gensym \"line\")) :res g?)\n          (% (2add-path?\n               (veq:f$_ (list (veq:f3-\n                                xy (veq:f3 1.0 8.0 (rnd:rnd)))\n                              (veq:f3+\n                                xy (veq:f3 1.0 2.0 (rnd:rnd)))))\n               :g g?)))))))\n```\n\n\n## Writing\n\nI have written about things related to this code 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  - https://inconvergent.net/2020/future-alterations/\n  - https://inconvergent.net/2021/future-alterations-and-loops/\n\nNote that these posts refer to older iterations of the code. So some of the\nthings will be out of date.\n\n![Boxes](img/boxes.png)\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\n`weird` depends on\n[cl-veq](https://github.com/inconvergent/cl-veq/releases/tag/v4.1.0-beta), and\nit requires Quicklisp to install other dependencies (which are listed in\n`weird.asd`).\n\nTo install and load `weird`, do:\n```lisp\n(ql:quickload :weird)\n```\nIf this does not work, `weird` may not be in a place Quicklisp or ASDF can see\nthem. To fix this, either:\n```lisp\n(load \"weird.asd\")\n```\nFor a long term solution, add the following to `.sbclrc`:\n```lisp\n#+quicklisp\n(push \"/path/to/dir/containing/weird\" ql:*local-project-directories*)\n```\nYou will have to make sure `cl-veq` is also available in the same fashion for\nany of this to work.\n\n### Versions and Compatability\n\nWeird version 6.1.0 requires version `cl-veq` 2.2.0.\n\n\n### Tests\n\nTests can be executed using: `(asdf:test-system :weird)`.\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). Although I have made some changes\nsince then.\n\nAlso, many thanks to https://twitter.com/xach for making Quicklisp.\n\n","funding_links":[],"categories":["Common Lisp"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finconvergent%2Fweird","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finconvergent%2Fweird","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finconvergent%2Fweird/lists"}