None of this is working very well; bit it is working a bit.
This commit is contained in:
parent
3551623374
commit
357d4c5853
20
README.md
20
README.md
|
@ -1,3 +1,19 @@
|
||||||
# tittle
|
# Tittle
|
||||||
|
|
||||||
|
Turtles in [Scittle](https://babashka.org/scittle/).
|
||||||
|
|
||||||
|
See [Papert, S: Mindstorms](https://www.worldofbooks.com/en-gb/products/mindstorms-book-seymour-papert-9780745016047).
|
||||||
|
|
||||||
|
## State of Play
|
||||||
|
|
||||||
|
As a proof of concept, this kind of proves the concept; but it doesn't work, the trigonometry functions are pretty badly broken.
|
||||||
|
|
||||||
|
I have not (yet) managed to get a scittle REPL running.
|
||||||
|
|
||||||
|
## Licence
|
||||||
|
|
||||||
|
Copyright © Simon Brooke 2025
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
|
||||||
Turtles in Scittle
|
|
8
deps.edn
Normal file
8
deps.edn
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{:deps {io.github.babashka/http-server
|
||||||
|
{:git/sha "b38c1f16ad2c618adae2c3b102a5520c261a7dd3"}
|
||||||
|
io.github.babashka/sci.nrepl {:mvn/version "0.0.2"}
|
||||||
|
http-kit/http-kit {:mvn/version "2.8.1"}
|
||||||
|
|
||||||
|
instaparse-cljs/instaparse-cljs {:mvn/version "1.2.2-SNAPSHOT"}
|
||||||
|
org.babashka/sci {:mvn/version "0.10.49"}
|
||||||
|
}}
|
222
index.html
Normal file
222
index.html
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en-GB">
|
||||||
|
<head>
|
||||||
|
<title>Tittle: Turtles in Scittle</title>
|
||||||
|
<script src="resources/js/scittle.js" type="application/javascript"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Tittle: Turtles in <a href="https://github.com/babashka/scittle/tree/main">Scittle</a></h1>
|
||||||
|
<svg id="playing-field"
|
||||||
|
stype="width: 2000; height: 2000"
|
||||||
|
version="1.1"
|
||||||
|
width="594mm"
|
||||||
|
height="420mm"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
<script type="application/x-scittle">
|
||||||
|
(def turtle (atom {:theta 0 :x 0 :y 0 :pen :up :ink "blue"}))
|
||||||
|
|
||||||
|
(defn log-turtle! []
|
||||||
|
(.log js/console
|
||||||
|
(apply str
|
||||||
|
(map #(str % " " (@turtle %) "; ") (keys @turtle)))))
|
||||||
|
|
||||||
|
(log-turtle!)
|
||||||
|
|
||||||
|
(def playing-field (.getElementById js/document "playing-field"))
|
||||||
|
|
||||||
|
(defn- not-a-number! [n]
|
||||||
|
(throw (js/Error. (str "not a number: " n))))
|
||||||
|
|
||||||
|
(defn number-or-error!
|
||||||
|
"If `n` is a number, return it, else throw an exception."
|
||||||
|
[n]
|
||||||
|
(cond (.isNaN js/Number n) (not-a-number! n)
|
||||||
|
(number? n) n
|
||||||
|
:else (not-a-number! n)))
|
||||||
|
|
||||||
|
(defn sanitise-angle
|
||||||
|
[angle]
|
||||||
|
(let [v (cond (.isNaN js/Number angle) 0
|
||||||
|
(< (abs angle) 0.5) 0
|
||||||
|
(< (abs angle) 360) angle
|
||||||
|
:else (rem 360 angle))]
|
||||||
|
(.log js/console (str "(sanitise-angle " angle ") -> " v))
|
||||||
|
v))
|
||||||
|
|
||||||
|
(defn turn!
|
||||||
|
"Turn the turtle clockwise by this `angle`, expressed in degrees with
|
||||||
|
respect to the X axis. If `angle` is not a number, throw an exception."
|
||||||
|
[angle]
|
||||||
|
(.info js/console (str "(turn! " angle ")"))
|
||||||
|
(if (number? angle)
|
||||||
|
(swap! turtle assoc :theta
|
||||||
|
(sanitise-angle
|
||||||
|
(rem 360 (+ (:theta @turtle) angle))))
|
||||||
|
(not-a-number! angle))
|
||||||
|
(.info js/console (str "(turn! " angle
|
||||||
|
") :: :theta now " (:theta @turtle)))
|
||||||
|
(log-turtle!))
|
||||||
|
|
||||||
|
(defn turn-to!
|
||||||
|
"Turn the turtle to face `angle`, expressed in degrees with respect to the
|
||||||
|
X axis. If `angle` is not a number, throw an exception."
|
||||||
|
[angle]
|
||||||
|
(.info js/console (str "(turn-to! " angle ")"))
|
||||||
|
(if (number? angle)
|
||||||
|
(swap! turtle assoc :theta (sanitise-angle angle))
|
||||||
|
(not-a-number! angle))
|
||||||
|
(.info js/console (str "(turn-to! " angle
|
||||||
|
") :: :theta now " (:theta @turtle))))
|
||||||
|
|
||||||
|
(defn pen-down!
|
||||||
|
"Put the turtle's pen down."
|
||||||
|
[]
|
||||||
|
(.info js/console "pen-down!")
|
||||||
|
(swap! turtle assoc :pen :down))
|
||||||
|
|
||||||
|
(defn pen-up!
|
||||||
|
"Lift the turtle's pen up"
|
||||||
|
[]
|
||||||
|
(.info js/console "pen-up!")
|
||||||
|
(swap! turtle assoc :pen :up))
|
||||||
|
|
||||||
|
(defn pen-down?
|
||||||
|
"Return `true` if the turtle's pen is down, else `false`."
|
||||||
|
[]
|
||||||
|
(let [v (= (@turtle :pen) :down)]
|
||||||
|
(.info js/console "(pen-down?) =>" v)
|
||||||
|
v))
|
||||||
|
|
||||||
|
(defn pen-up?
|
||||||
|
"Return `true` if the turtle's pen is not down, else `false`."
|
||||||
|
[]
|
||||||
|
(not (pen-down?)))
|
||||||
|
|
||||||
|
(defn move-to!
|
||||||
|
"Move the turtle absolutely to the coordinates `x`, `y`."
|
||||||
|
[x y]
|
||||||
|
(.info js/console (str "(move-to! " x " " y ")"))
|
||||||
|
(when (map number-or-error! [x y])
|
||||||
|
(let [x' (:x @turtle)
|
||||||
|
y' (:y @turtle)]
|
||||||
|
(when (pen-down?)
|
||||||
|
(let [elt (.createElementNS js/document "http://www.w3.org/2000/svg" "line")
|
||||||
|
id (gensym "line")]
|
||||||
|
(.setAttribute elt "id" id)
|
||||||
|
(.setAttribute elt "x1" x')
|
||||||
|
(.setAttribute elt "y1" y')
|
||||||
|
(.setAttribute elt "x2" x)
|
||||||
|
(.setAttribute elt "y2" y)
|
||||||
|
(.setAttribute elt "stroke" (or (:ink @turtle) "blue"))
|
||||||
|
(.appendChild playing-field elt)))
|
||||||
|
(swap! turtle assoc :x x :y y))))
|
||||||
|
|
||||||
|
(def ^:const pi 3.141592653589793)
|
||||||
|
|
||||||
|
(defn degrees->radians
|
||||||
|
"Return the equivalent, in radians, of this `angle` espressed in degrees"
|
||||||
|
[angle]
|
||||||
|
(let [v (when (number-or-error! angle)
|
||||||
|
(* angle (/ pi 180)))]
|
||||||
|
(.log js/console (str "(degrees->radians " angle ") => " v))
|
||||||
|
v))
|
||||||
|
|
||||||
|
(defn radians->degrees
|
||||||
|
"Return the equivalent, in degrees, of this `angle` espressed in radians"
|
||||||
|
[angle]
|
||||||
|
(let [v (when (number-or-error! angle)
|
||||||
|
(/ (* angle 180) pi))]
|
||||||
|
(.log js/console (str "(radians->degrees " angle ") => " v))
|
||||||
|
v))
|
||||||
|
|
||||||
|
(defn sin
|
||||||
|
"Return the sine of this `angle`, considered to be expressed in degrees."
|
||||||
|
[angle]
|
||||||
|
(let [v (.sin js/Math (degrees->radians angle))]
|
||||||
|
(.log js/console (str "(sin " angle ") => " v))
|
||||||
|
v))
|
||||||
|
|
||||||
|
(defn cos
|
||||||
|
"Return the cosine of this `angle`, considered to be expressed in degrees."
|
||||||
|
[angle]
|
||||||
|
(let [v (.cos js/Math (degrees->radians angle))]
|
||||||
|
(.log js/console (str "(cos " angle ") => " v))
|
||||||
|
v))
|
||||||
|
|
||||||
|
(defn move!
|
||||||
|
"Move the turtle forward on its current heading by `distance` units."
|
||||||
|
[distance]
|
||||||
|
(.info js/console (str "(move! " distance ")"))
|
||||||
|
(move-to! (+ (:x @turtle) (* distance (sin (:theta @turtle))))
|
||||||
|
(+ (:y @turtle) (* distance (cos (:theta @turtle))))))
|
||||||
|
|
||||||
|
(defn set-ink!
|
||||||
|
"Set the ink with which the turtle draws to this `colour`, which
|
||||||
|
should be a string representation of a colour known to CSS."
|
||||||
|
[colour]
|
||||||
|
(.log js/console (str "(set-ink! " colour ")"))
|
||||||
|
(when (string? colour)
|
||||||
|
(swap! turtle assoc :ink colour)))
|
||||||
|
|
||||||
|
(defn draw-tree!
|
||||||
|
[length left-branch right-branch curvature branch-fraction trunk-fraction depth]
|
||||||
|
(log-turtle!)
|
||||||
|
(when (> depth 0)
|
||||||
|
(pen-down!)
|
||||||
|
(set-ink! (if (<= depth 2) "green" "brown"))
|
||||||
|
(move! length)
|
||||||
|
(turn! curvature)
|
||||||
|
(draw-tree! (* length trunk-fraction)
|
||||||
|
(* left-branch branch-fraction)
|
||||||
|
(* right-branch branch-fraction)
|
||||||
|
curvature
|
||||||
|
branch-fraction
|
||||||
|
trunk-fraction
|
||||||
|
(dec depth))
|
||||||
|
(turn! (- 0 (+ curvature left-branch)))
|
||||||
|
(draw-tree! (* length trunk-fraction)
|
||||||
|
left-branch
|
||||||
|
right-branch
|
||||||
|
curvature
|
||||||
|
branch-fraction
|
||||||
|
trunk-fraction
|
||||||
|
(dec depth))
|
||||||
|
(turn! (+ left-branch right-branch))
|
||||||
|
(draw-tree! (* length trunk-fraction)
|
||||||
|
left-branch
|
||||||
|
right-branch
|
||||||
|
curvature
|
||||||
|
branch-fraction
|
||||||
|
trunk-fraction
|
||||||
|
(dec depth))
|
||||||
|
(turn! (- 0 right-branch curvature))
|
||||||
|
(pen-up!)
|
||||||
|
(move! (- 0 length))))
|
||||||
|
|
||||||
|
(defn draw-polygon!
|
||||||
|
[sides side-length]
|
||||||
|
(when {<= 3 sides 360}
|
||||||
|
(let [angle (/ 360 sides)]
|
||||||
|
(pen-down!)
|
||||||
|
(loop [side 0]
|
||||||
|
(turn! angle)
|
||||||
|
(move! side-length)
|
||||||
|
(when (< side sides)
|
||||||
|
(recur (inc side))))
|
||||||
|
(pen-up!))))
|
||||||
|
|
||||||
|
(log-turtle!)
|
||||||
|
(pen-up!)
|
||||||
|
(log-turtle!)
|
||||||
|
(move-to! 500 500)
|
||||||
|
(log-turtle!)
|
||||||
|
(turn-to! 180)
|
||||||
|
;; (draw-tree! 100 70 60 5 0.25 0.7 3)
|
||||||
|
;; (map #(draw-polygon! % 100) (range 3 20))
|
||||||
|
(draw-polygon! 3 100)
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
20
resources/cljs/clojure/string.cljs
Normal file
20
resources/cljs/clojure/string.cljs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
(ns clojure.string)
|
||||||
|
|
||||||
|
;; NOTE NOTE NOTE! This is not the real `clojure.string`. It dummies enough
|
||||||
|
;; of `clojure.string` to get other libraries I'm using running!
|
||||||
|
|
||||||
|
(defn escape
|
||||||
|
"Escape characters in `s` which HTML cannot be certain to directly render,
|
||||||
|
using hints from these `char-escapes`, which is expected to map characters
|
||||||
|
to string representations of appropriate HTML entity substutions."
|
||||||
|
[s char-escapes]
|
||||||
|
(apply str (map #(let [c (int %)
|
||||||
|
m (char-escapes %)]
|
||||||
|
(cond m m
|
||||||
|
(<= 32 c 126) %
|
||||||
|
:else (str "&#" c ";"))) s)))
|
||||||
|
|
||||||
|
(defn replace
|
||||||
|
"Replace all occurences of this `pattern` in this string `s` with this `replace`ment."
|
||||||
|
[s pattern replace]
|
||||||
|
(.replaceAll s pattern replace))
|
103
resources/cljs/hiccups/runtime.cljs
Normal file
103
resources/cljs/hiccups/runtime.cljs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
(ns hiccups.runtime
|
||||||
|
(:require [clojure.string :as cstring]))
|
||||||
|
|
||||||
|
;; this is copied/adapted from https://github.com/teropa/hiccups/tree/master
|
||||||
|
;; basically to see how hard it is to get mainstream CLJS libraries running
|
||||||
|
;; in Scittle.
|
||||||
|
;;
|
||||||
|
;; Copyright Tero Parviainen, licence Eclipse Public License.
|
||||||
|
|
||||||
|
;; The original requires `[clojure.string :as cstring]`, which of course I
|
||||||
|
;; can't do.
|
||||||
|
|
||||||
|
(def ^{:doc "Regular expression that parses a CSS-style id and class from a tag name." :private true}
|
||||||
|
re-tag #"([^\s\.#]+)(?:#([^\s\.#]+))?(?:\.([^\s#]+))?")
|
||||||
|
|
||||||
|
(def ^{:doc "Characters to replace when escaping HTML" :private true}
|
||||||
|
character-escapes {\& "&", \< "<", \> ">", \" """})
|
||||||
|
|
||||||
|
(def ^{:doc "A list of tags that need an explicit ending tag when rendered."}
|
||||||
|
container-tags
|
||||||
|
#{"a" "b" "body" "canvas" "dd" "div" "dl" "dt" "em" "fieldset" "form" "h1" "h2" "h3"
|
||||||
|
"h4" "h5" "h6" "head" "html" "i" "iframe" "label" "li" "ol" "option" "pre"
|
||||||
|
"script" "span" "strong" "style" "table" "textarea" "ul"})
|
||||||
|
|
||||||
|
(defn as-str [x]
|
||||||
|
(if (or (keyword? x) (symbol? x))
|
||||||
|
(name x)
|
||||||
|
(str x)))
|
||||||
|
|
||||||
|
(def ^:dynamic *html-mode* :xml)
|
||||||
|
|
||||||
|
(defn- xml-mode? []
|
||||||
|
(= *html-mode* :xml))
|
||||||
|
|
||||||
|
(defn in-mode [mode f]
|
||||||
|
(binding [*html-mode* mode]
|
||||||
|
(f)))
|
||||||
|
|
||||||
|
(defn escape-html
|
||||||
|
"Change special characters into HTML character entities."
|
||||||
|
[text]
|
||||||
|
(-> (as-str text)
|
||||||
|
(cstring/escape character-escapes)))
|
||||||
|
|
||||||
|
(def h escape-html) ; alias for escape-html
|
||||||
|
|
||||||
|
(defn end-tag []
|
||||||
|
(if (xml-mode?) " />" ">"))
|
||||||
|
|
||||||
|
(defn xml-attribute
|
||||||
|
([name value] (xml-attribute name value true))
|
||||||
|
([name value escape?]
|
||||||
|
(str " " (as-str name) "=\"" (if escape? (escape-html value) value) "\"")))
|
||||||
|
|
||||||
|
(declare render-attr-map)
|
||||||
|
|
||||||
|
(defn render-attribute [[name value]]
|
||||||
|
(cond
|
||||||
|
(true? value)
|
||||||
|
(if (xml-mode?)
|
||||||
|
(xml-attribute name name)
|
||||||
|
(str " " (as-str name)))
|
||||||
|
(not value)
|
||||||
|
""
|
||||||
|
:else
|
||||||
|
(xml-attribute name (if (map? value) (render-attr-map value) value) false)))
|
||||||
|
|
||||||
|
(defn render-attr-map [attrs]
|
||||||
|
(apply str
|
||||||
|
(sort (map render-attribute attrs))))
|
||||||
|
|
||||||
|
(defn normalize-element
|
||||||
|
"Ensure a tag vector is of the form [tag-name attrs content]."
|
||||||
|
[[tag & content]]
|
||||||
|
(when (not (or (keyword? tag) (symbol? tag) (string? tag)))
|
||||||
|
(throw (str tag " is not a valid tag name")))
|
||||||
|
(let [[_ tag id class] (re-matches re-tag (as-str tag))
|
||||||
|
tag-attrs {:id id
|
||||||
|
:class (if class (cstring/replace class "." " "))}
|
||||||
|
map-attrs (first content)]
|
||||||
|
(if (map? map-attrs)
|
||||||
|
[tag (merge tag-attrs map-attrs) (next content)]
|
||||||
|
[tag tag-attrs content])))
|
||||||
|
|
||||||
|
(declare render-html)
|
||||||
|
|
||||||
|
(defn render-element
|
||||||
|
"Render a tag vector as a HTML element."
|
||||||
|
[element]
|
||||||
|
(let [[tag attrs content] (normalize-element element)]
|
||||||
|
(if (or content (container-tags tag))
|
||||||
|
(str "<" tag (render-attr-map attrs) ">"
|
||||||
|
(render-html content)
|
||||||
|
"</" tag ">")
|
||||||
|
(str "<" tag (render-attr-map attrs) (end-tag)))))
|
||||||
|
|
||||||
|
(defn render-html
|
||||||
|
"Turn a Clojure data type into a string of HTML."
|
||||||
|
[x]
|
||||||
|
(cond
|
||||||
|
(vector? x) (render-element x)
|
||||||
|
(seq? x) (apply str (map render-html x))
|
||||||
|
:else (as-str x)))
|
203
resources/cljs/tittle.cljs
Normal file
203
resources/cljs/tittle.cljs
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
(ns tittle)
|
||||||
|
|
||||||
|
(def turtle (atom {:theta 0 :x 0 :y 0 :pen :up :ink "blue"}))
|
||||||
|
|
||||||
|
(defn log-turtle! []
|
||||||
|
(.log js/console
|
||||||
|
(apply str
|
||||||
|
(map #(str % " " (@turtle %) "; ") (keys @turtle)))))
|
||||||
|
|
||||||
|
(log-turtle!)
|
||||||
|
|
||||||
|
(def playing-field (.getElementById js/document "playing-field"))
|
||||||
|
|
||||||
|
(defn- not-a-number! [n]
|
||||||
|
(throw (js/Error. (str "not a number: " n))))
|
||||||
|
|
||||||
|
(defn number-or-error!
|
||||||
|
"If `n` is a number, return it, else throw an exception."
|
||||||
|
[n]
|
||||||
|
(cond (.isNaN js/Number n) (not-a-number! n)
|
||||||
|
(number? n) n
|
||||||
|
:else (not-a-number! n)))
|
||||||
|
|
||||||
|
(defn sanitise-angle
|
||||||
|
[angle]
|
||||||
|
(let [v (cond (.isNaN js/Number angle) 0
|
||||||
|
(< (abs angle) 0.5) 0
|
||||||
|
(< (abs angle) 360) angle
|
||||||
|
:else (rem 360 angle))]
|
||||||
|
(.log js/console (str "(sanitise-angle " angle ") -> " v))
|
||||||
|
v))
|
||||||
|
|
||||||
|
(defn turn!
|
||||||
|
"Turn the turtle clockwise by this `angle`, expressed in degrees with
|
||||||
|
respect to the X axis. If `angle` is not a number, throw an exception."
|
||||||
|
[angle]
|
||||||
|
(.info js/console (str "(turn! " angle ")"))
|
||||||
|
(if (number? angle)
|
||||||
|
(swap! turtle assoc :theta
|
||||||
|
(sanitise-angle
|
||||||
|
(rem 360 (+ (:theta @turtle) angle))))
|
||||||
|
(not-a-number! angle))
|
||||||
|
(.info js/console (str "(turn! " angle
|
||||||
|
") :: :theta now " (:theta @turtle)))
|
||||||
|
(log-turtle!))
|
||||||
|
|
||||||
|
(defn turn-to!
|
||||||
|
"Turn the turtle to face `angle`, expressed in degrees with respect to the
|
||||||
|
X axis. If `angle` is not a number, throw an exception."
|
||||||
|
[angle]
|
||||||
|
(.info js/console (str "(turn-to! " angle ")"))
|
||||||
|
(if (number? angle)
|
||||||
|
(swap! turtle assoc :theta (sanitise-angle angle))
|
||||||
|
(not-a-number! angle))
|
||||||
|
(.info js/console (str "(turn-to! " angle
|
||||||
|
") :: :theta now " (:theta @turtle))))
|
||||||
|
|
||||||
|
(defn pen-down!
|
||||||
|
"Put the turtle's pen down."
|
||||||
|
[]
|
||||||
|
(.info js/console "pen-down!")
|
||||||
|
(swap! turtle assoc :pen :down))
|
||||||
|
|
||||||
|
(defn pen-up!
|
||||||
|
"Lift the turtle's pen up"
|
||||||
|
[]
|
||||||
|
(.info js/console "pen-up!")
|
||||||
|
(swap! turtle assoc :pen :up))
|
||||||
|
|
||||||
|
(defn pen-down?
|
||||||
|
"Return `true` if the turtle's pen is down, else `false`."
|
||||||
|
[]
|
||||||
|
(let [v (= (@turtle :pen) :down)]
|
||||||
|
(.info js/console "(pen-down?) =>" v)
|
||||||
|
v))
|
||||||
|
|
||||||
|
(defn pen-up?
|
||||||
|
"Return `true` if the turtle's pen is not down, else `false`."
|
||||||
|
[]
|
||||||
|
(not (pen-down?)))
|
||||||
|
|
||||||
|
(defn move-to!
|
||||||
|
"Move the turtle absolutely to the coordinates `x`, `y`."
|
||||||
|
[x y]
|
||||||
|
(.info js/console (str "(move-to! " x " " y ")"))
|
||||||
|
(when (map number-or-error! [x y])
|
||||||
|
(let [x' (:x @turtle)
|
||||||
|
y' (:y @turtle)]
|
||||||
|
(when (pen-down?)
|
||||||
|
(let [elt (.createElementNS js/document "http://www.w3.org/2000/svg" "line")
|
||||||
|
id (gensym "line")]
|
||||||
|
(.setAttribute elt "id" id)
|
||||||
|
(.setAttribute elt "x1" x')
|
||||||
|
(.setAttribute elt "y1" y')
|
||||||
|
(.setAttribute elt "x2" x)
|
||||||
|
(.setAttribute elt "y2" y)
|
||||||
|
(.setAttribute elt "stroke" (or (:ink @turtle) "blue"))
|
||||||
|
(.appendChild playing-field elt)))
|
||||||
|
(swap! turtle assoc :x x :y y))))
|
||||||
|
|
||||||
|
(def ^:const pi 3.141592653589793)
|
||||||
|
|
||||||
|
(defn degrees->radians
|
||||||
|
"Return the equivalent, in radians, of this `angle` espressed in degrees"
|
||||||
|
[angle]
|
||||||
|
(let [v (when (number-or-error! angle)
|
||||||
|
(* angle (/ pi 180)))]
|
||||||
|
(.log js/console (str "(degrees->radians " angle ") => " v))
|
||||||
|
v))
|
||||||
|
|
||||||
|
(defn radians->degrees
|
||||||
|
"Return the equivalent, in degrees, of this `angle` espressed in radians"
|
||||||
|
[angle]
|
||||||
|
(let [v (when (number-or-error! angle)
|
||||||
|
(/ (* angle 180) pi))]
|
||||||
|
(.log js/console (str "(radians->degrees " angle ") => " v))
|
||||||
|
v))
|
||||||
|
|
||||||
|
(defn sin
|
||||||
|
"Return the sine of this `angle`, considered to be expressed in degrees."
|
||||||
|
[angle]
|
||||||
|
(let [v (.sin js/Math (degrees->radians angle))]
|
||||||
|
(.log js/console (str "(sin " angle ") => " v))
|
||||||
|
v))
|
||||||
|
|
||||||
|
(defn cos
|
||||||
|
"Return the cosine of this `angle`, considered to be expressed in degrees."
|
||||||
|
[angle]
|
||||||
|
(let [v (.cos js/Math (degrees->radians angle))]
|
||||||
|
(.log js/console (str "(cos " angle ") => " v))
|
||||||
|
v))
|
||||||
|
|
||||||
|
(defn move!
|
||||||
|
"Move the turtle forward on its current heading by `distance` units."
|
||||||
|
[distance]
|
||||||
|
(.info js/console (str "(move! " distance ")"))
|
||||||
|
(move-to! (+ (:x @turtle) (* distance (sin (:theta @turtle))))
|
||||||
|
(+ (:y @turtle) (* distance (cos (:theta @turtle))))))
|
||||||
|
|
||||||
|
(defn set-ink!
|
||||||
|
"Set the ink with which the turtle draws to this `colour`, which
|
||||||
|
should be a string representation of a colour known to CSS."
|
||||||
|
[colour]
|
||||||
|
(.log js/console (str "(set-ink! " colour ")"))
|
||||||
|
(when (string? colour)
|
||||||
|
(swap! turtle assoc :ink colour)))
|
||||||
|
|
||||||
|
(defn draw-tree!
|
||||||
|
[length left-branch right-branch curvature branch-fraction trunk-fraction depth]
|
||||||
|
(log-turtle!)
|
||||||
|
(when (> depth 0)
|
||||||
|
(pen-down!)
|
||||||
|
(set-ink! (if (<= depth 2) "green" "brown"))
|
||||||
|
(move! length)
|
||||||
|
(turn! curvature)
|
||||||
|
(draw-tree! (* length trunk-fraction)
|
||||||
|
(* left-branch branch-fraction)
|
||||||
|
(* right-branch branch-fraction)
|
||||||
|
curvature
|
||||||
|
branch-fraction
|
||||||
|
trunk-fraction
|
||||||
|
(dec depth))
|
||||||
|
(turn! (- 0 (+ curvature left-branch)))
|
||||||
|
(draw-tree! (* length trunk-fraction)
|
||||||
|
left-branch
|
||||||
|
right-branch
|
||||||
|
curvature
|
||||||
|
branch-fraction
|
||||||
|
trunk-fraction
|
||||||
|
(dec depth))
|
||||||
|
(turn! (+ left-branch right-branch))
|
||||||
|
(draw-tree! (* length trunk-fraction)
|
||||||
|
left-branch
|
||||||
|
right-branch
|
||||||
|
curvature
|
||||||
|
branch-fraction
|
||||||
|
trunk-fraction
|
||||||
|
(dec depth))
|
||||||
|
(turn! (- 0 right-branch curvature))
|
||||||
|
(pen-up!)
|
||||||
|
(move! (- 0 length))))
|
||||||
|
|
||||||
|
(defn draw-polygon!
|
||||||
|
[sides side-length]
|
||||||
|
(when {<= 3 sides 360}
|
||||||
|
(let [angle (/ 360 sides)]
|
||||||
|
(pen-down!)
|
||||||
|
(loop [side 0]
|
||||||
|
(turn! angle)
|
||||||
|
(move! side-length)
|
||||||
|
(when (< side sides)
|
||||||
|
(recur (inc side))))
|
||||||
|
(pen-up!))))
|
||||||
|
|
||||||
|
(log-turtle!)
|
||||||
|
(pen-up!)
|
||||||
|
(log-turtle!)
|
||||||
|
(move-to! 500 500)
|
||||||
|
(log-turtle!)
|
||||||
|
(turn-to! 180)
|
||||||
|
;; (draw-tree! 100 70 60 5 0.25 0.7 3)
|
||||||
|
;; (map #(draw-polygon! % 100) (range 3 20))
|
||||||
|
(draw-polygon! 3 100)
|
1944
resources/js/scittle.js
Normal file
1944
resources/js/scittle.js
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue