((section 2 "Outdated egg!" (p "This is an egg for CHICKEN 4, the unsupported old release.  You're almost certainly looking for " (int-link "/eggref/5/linden-scheme" "the CHICKEN 5 version of this egg") ", if it exists.") (p "If it does not exist, there may be equivalent functionality provided by another egg; have a look at the " (link "https://wiki.call-cc.org/chicken-projects/egg-index-5.html" "egg index") ". Otherwise, please consider porting this egg to the current version of CHICKEN.")) (section 2 "linden-scheme" (toc) (p "Parametric 2L-systems integrated with Scheme. The L-systems defined with this library take a significantly different form from the string-based L-systems described by Lindenmayer. linden-scheme uses a class-based system for defining L-systems.") (p (image-link "http://alex-charlton.com/static/images/linden-scheme-tree-example.png")) (section 3 "Requirements" (ul (li "gl-math"))) (section 3 "Documentation" (section 4 "Defining L-systems" (p "L-systems in linden-scheme are comprised of two parts: L-systems and rules, which are conceptually analogous to classes and methods, respectively.") (def (sig (syntax " (define-rule [CLASS] (RULE-NAME [ARGS] ...) BODY ...)" (id define-rule))) (p "Defines a rule similar to defining a function, but with an optional " (tt "CLASS") ". If " (tt "CLASS") " is omitted, the rule will be used if no more specific rules are defined – i.e. as a fallback rule. Multiple rules with the same " (tt "RULE-NAME") " may be defined, provided they have different classes (otherwise the previous rule with the same name will be over-written).") (p "Rules should return a list of rules in " (tt "(RULE [ARGS] ...)") " form. Any non-list value, when returned, will make evaluation of the rule treated as a no-op. In other words, when the rule is evaluated and a list is returned, the rule being evaluated in the current L-systems (by " (tt "step-l-system") ") will be replaced by the given list of rules. If no list is provided, the rule being evaluated will remain the same.") (p "The special form " (tt "branch") " may be included in the list of rules returned by a rule. " (tt "branch") " should contain one or more rules. When " (tt "branch") " is used, the state of the rules contained in the " (tt "branch") " is split off from that of the parent of the " (tt "branch") ".") (p "For example, a list returned by a rule may look like:") (pre "   '((leaf 1)\n     (branch (leaf 1) (stem 1) (apex))\n     (stem 1) \n     (apex))") (p "which describes a leaf, a branch (containing a leaf, stem, and apex), a stem, and an apex.") (p "When context-dependant or probabilistic rules are desired, see the macros " (tt "context") " and " (tt "probability") " in the section " (int-link "#macros" "Macros") ".")) (def (sig (syntax " (define-render-rule [CLASS] (RULE-NAME [ARGS] ...) BODY ...)" (id define-render-rule))) (p (tt "define-render-rule") " behaves much like " (tt "define-rule") ", to be used for the rendering of an L-system. Render rules operate by side-effect only. The parameter " (tt "render-target") " is provided to track the object that the L-system is rendering to. See " (tt "render-l-system") " and " (int-link "#turtle-graphics" "Turtle graphics") ".")) (def (sig (syntax " (define-l-system CLASS (SUPER-CLASSES ...) (RULE [ARGS] ...))" (id define-l-system))) (p "Defines the class of L-systems named " (tt "CLASS") ". These L-systems will use rules defined for " (tt "CLASS") ", and if none are available, they will inherit those from the classes " (tt "SUPER-CLASSES") ". The rules defined for the super-classes are chosen in order of appearance in the super-classes list.") (p "Additionally a function named " (tt "CLASS") " is defined that, when called, returns an L-system of " (tt "CLASS") " with the initial rule " (tt "RULE") "."))) (section 4 "Stepping and rendering L-systems" (def (sig (procedure " (step-l-system SYSTEM)" (id step-l-system))) (p "Returns a new L-system, created by evaluating each rule in " (tt "SYSTEM") " according to the rules defined by " (tt "define-rule") " corresponding to the class of the L-system. Any rule that has not been defined by " (tt "define-rule") " for the class or super-classes of the system is ignored. When rules are being evaluated, any state is branched according to " (tt "branch") " statements in the system (see " (int-link "#manipulating-state" "Manipulating state") ").")) (def (sig (procedure " (step-l-system-times N SYSTEM)" (id step-l-system-times))) (p "Performs " (tt "step-l-system") " on the given " (tt "SYSTEM") ", " (tt "N") " times.")) (def (sig (procedure " (render-l-system SYSTEM RENDER-TARGET)" (id render-l-system))) (p "Evaluates each rule in the " (tt "SYSTEM") ", in order, given their meanings defined by " (tt "define-render-rule") ". Any rule that has not been defined by " (tt "define-render-rule") " for the class or super-classes of the system is ignored. As with " (tt "step-l-system") ", state is branched according to the " (tt "branch") " statements in the system. The parameter " (tt "render-target") " is set to " (tt "RENDER-TARGET") " at the start of evaluation (see " (int-link "#turtle-graphics" "Turtle graphics") "). The value of " (tt "render-target") " is returned."))) (section 4 "Macros" (def (sig (syntax " (context (TEST BODY ...) ...)" (id context))) (p "Similar to a " (tt "cond") " form, but conditional to the context of a given rule. Evaluates " (tt "BODY") " when the context given in " (tt "TEST") " matches the current context of the rule being evaluated. Each " (tt "TEST") " should be of the form:") (pre "    ((PREVIOUS-RULE [ARGS] ...) (NEXT-RULE [ARGS] ...) [: GUARD])") (p "When " (tt "PREVIOUS-RULE") " and " (tt "NEXT-RULE") " match the names of the previous and next rules to the rule being currently evaluated, the associated " (tt "BODY") " is evaluated at the exclusion of all other " (tt "BODY") "s. The supplied " (tt "ARGS") " are symbols that are bound to the values of the arguments of their respective rules. Either " (tt "(PREVIOUS-RULE [ARGS] ...)") " or " (tt "(NEXT-RULE [ARGS] ...)") " may be replaced with a " (tt "*") ", indicating that any rule (including none) may match.") (p "If desired, a " (tt "GUARD") " form may be supplied, preceded by a " (tt ":") ". This guard acts as an additional test before a " (tt "BODY") " is evaluated. The guard may use any variables given as " (tt "ARGS") " (as well as any other variables in scope).") (p "The last " (tt "TEST") " may consist of the symbol " (tt "else") ", which is unconditionally evaluated.") (p "For example:") (pre "   (define-rule (apex)\n     (context\n      (((stem len) * : (> len 2))\n       '((leaf 1) (branch (leaf 1) (stem 1) (apex)) (stem 1) (apex)))\n      (((stem len) *)\n       #f)\n      (else '((leaf 1) (stem 1) (apex)))))") (p "defines a rule (" (tt "apex") ") that creates a new branch when preceded by a " (tt "stem") " whose first parameter (" (tt "len") ") greater than 2, does nothing when preceded by a " (tt "stem") " of length less than or equal to 2, and otherwise creates a new " (tt "leaf") " and " (tt "stem") ".")) (def (sig (syntax " (probability (PROBABILITY BODY ...) ...)" (id probability))) (p "Similar to a " (tt "cond") " form, but evaluates a clause based on a given probability. " (tt "PROBABILITY") " is expected to be a number less than 1.0, or the symbol " (tt "else") ". The sum of all probabilities should add up to 1.0, or less than 1.0 if an " (tt "else") " clause is used. Otherwise there is a chance that no clauses will be evaluated (in the case that the probabilities add to less than 1.0 and no " (tt "else") " is used) or a clause may never be evaluated (in the case that the probabilities add to more than 1.0).") (p "For example:") (pre "   (probability\n     (0.1 '((branch (flower)) (apex)))\n     (0.7 #f)\n     (else '((branch (flower)) (branch (flower)) (apex))) ") (p "describes a 10% chance of creating a branch with a flower, a 70% chance of doing nothing, and a 20% chance of creating two branches with flowers."))) (section 4 "Manipulating state" (p "While rendering L-systems, it is often desirable to track the state of a number of variables, following the branches in the L-system. In this manner, one can implement any type of turtle graphics system. linden-scheme provides this mechanism through the following three functions:") (def (sig (procedure " (define-state VAR DEFAULT)" (id define-state))) (p "Creates a new state variable named " (tt "VAR") " with the default value " (tt "DEFAULT") ".")) (def (sig (procedure " (get-state VAR)" (id get-state))) (p "Returns the value of the state variable " (tt "VAR") ". This is only useful when called within a rule.")) (def (sig (procedure " (set-state VAR VALUE)" (id set-state))) (p "Sets the value of the state variable " (tt "VAR") " to " (tt "VALUE") ". This is only useful when called within a rule."))) (section 4 "Turtle graphics" (p "linden-scheme provides a set of functions (often also render rules) that implement the standard turtle-graphics commands typically associated with graphical L-systems. These commands are intended to be a standard foundation for turtle graphics, but do not provide any graphical output mechanism themselves. Rather, they provide a convenient parameter for holding some sort of rendering target, as well as a set of procedures that are used to modify and access the usual geometric qualities: namely translation/rotation matrices and thickness. Two state variables are therefore defined for the turtle graphics system: " (tt "transform-matrix") ", " (tt "rotation-matrix") ", and " (tt "thickness") ", although facilities are provided so they do not need to be accessed through " (tt "get-state") " or " (tt "set-state") ".") (def (sig (parameter " render-target" (id render-target))) (p "Used to hold whatever the render rules are rendering to. Is set by " (tt "render-l-system") ".")) (def (sig (procedure " (transform-matrix)" (id transform-matrix))) (p "Returns the current transformation matrix, as modified by calls to " (tt "pitch") ", " (tt "roll") ", " (tt "turn") ", " (tt "move") ", and " (tt "move-forward") ".")) (def (sig (procedure " (rotation-matrix)" (id rotation-matrix))) (p "Returns the rotation component of the current transformation matrix, as modified by calls to " (tt "pitch") ", " (tt "roll") ", and " (tt "turn") ".")) (def (sig (procedure " (pitch ANGLE)" (id pitch))) (p "Transform the current transformation matrix by a rotation of " (tt "ANGLE") " degrees around the x-axis. Also defined as a render rule.")) (def (sig (procedure " (roll ANGLE)" (id roll))) (p "Transform the current transformation matrix by a rotation of " (tt "ANGLE") " degrees around the y-axis. Also defined as a render rule.")) (def (sig (procedure " (turn ANGLE)" (id turn))) (p "Transform the current transformation matrix by a rotation of " (tt "ANGLE") " degrees around the z-axis. Also defined as a render rule.")) (def (sig (procedure " (move X Y Z)" (id move))) (p "Transform the current transformation matrix by a translation of " (tt "(X Y Z)") ". Also defined as a render rule.")) (def (sig (procedure " (move-forward DISTANCE)" (id move-forward))) (p "Transform the current transformation matrix by a translation of " (tt "(0 DISTANCE 0)") ". Also defined as a render rule.")) (def (sig (procedure " (thickness [X])" (id thickness))) (p "Given the value " (tt "X") ", sets the state variable " (tt "thickness") " to " (tt "X") ". If " (tt "X") " is omitted, the value of " (tt "thickness") " is returned. Also defined as a render rule.")) (def (sig (procedure " (grow SCALE)" (id grow))) (p "Multiplies the value of the state variable " (tt "thickness") " by " (tt "SCALE") ". Also defined as a render rule.")))) (section 3 "Examples" (p "The following is an example of a L-system of a crocus taken from " (i "The Algorithmic Beauty of Plants") " " (link "http://algorithmicbotany.org/papers/abop/abop.pdf" "(Prusinkiewicz, Lindermayer. 1990)") ", section 3.1.3.") (p "Note that three different classes (" (tt "crocus") ", " (tt "plant") ", and none) are used in this example. If, for example, a new rule " (tt "stem") " were defined for the class " (tt "crocus") ", it would be used over the " (tt "stem") " rule that is defined for " (tt "plant") ".") (highlight scheme "    \n(use linden-scheme srfi-1)\n\n(define developmental-switch-time 3)\n(define leaf-growth-limit 4)\n(define flower-growth-limit 2)\n\n(define-rule crocus (apex time)\n  (cond\n     ((< time developmental-switch-time)\n      `((stem 1)\n        (branch (pitch -30) (leaf 0))\n        (roll 138)\n        (apex ,(add1 time))))\n     (else '((stem 20) (flower 0)))))\n\n(define-rule crocus (flower size)\n    (cond\n     ((< size flower-growth-limit)\n      `((flower ,(add1 size))))))\n\n(define-rule plant (stem length)\n  (cond\n   ((< length 4) `((stem ,(add1 length))))))\n\n(define-rule (leaf size)\n    (cond\n     ((< size leaf-growth-limit)\n      `((leaf ,(add1 size))))))\n\n(define-l-system crocus (plant)\n  (apex 1))\n\n(for-each (lambda (i)\n            (print (step-l-system-times i (crocus))))\n          (iota 5 1))") (p "For another, more visual example corresponding to the image at the top of this document, see the " (link "https://github.com/AlexCharlton/linden-scheme/tree/master/examples" "examples directory") ".")) (section 3 "Version history" (section 4 "Version 0.2.0" (p "24 April 2016") (ul (li "Add " (tt "rotation-matrix")))) (section 4 "Version 0.1.0" (ul (li "Initial release")))) (section 3 "Source repository" (p "Source available on " (link "https://github.com/AlexCharlton/linden-scheme" "GitHub") ".") (p "Bug reports and patches welcome! Bugs can be reported via GitHub or to alex.n.charlton at gmail.")) (section 3 "Author" (p "Alex Charlton")) (section 3 "License" (p "BSD"))))