Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
In 1901, Werner Boy (under direction from David Hilbert) discovered a non-orienting surface which is a self-intersecting immersion of the real projective plane in 3-dimensional space. The surface is obtained by sewing a Möbius strip to the edge of a disk, given by the parametric equations below.
(ns enchilada.boys-surface
(:require
[enchilada :refer [canvas ctx value-of canvas-size]]
[wireframes.renderer.canvas :refer [draw-solid ->canvas]]
[wireframes.renderer.color :refer [wireframe solid]]
[wireframes.transform :refer [point combine rotate scale translate degrees->radians]]
[wireframes.shapes.platonic-solids :refer [cube tetrahedron icosahedron dodecahedron]]
[wireframes.shapes.primitives :refer [mesh transform-shape center-at-origin]]
[jayq.core :refer [show]]
[inkspot.color :refer [coerce]]
[monet.canvas :refer [get-context fill-rect fill-style]]
[big-bang.core :refer [big-bang]]))
(def root2 (Math/sqrt 2))
(def Π Math/PI)
(def sin Math/sin)
(def cos Math/cos)
(defn make-boys-surface [divisions alpha]
(let [i (/ Π divisions)
d (fn [u v] (- 2 (* alpha root2 (sin (* 3 u)) (sin (* 2 v)))))
x (fn [u v] (+ (* root2 (cos v) (cos v) (cos (* 2 u))) (* (cos u) (sin (* 2 v)))))
y (fn [u v] (- (* root2 (cos v) (cos v) (sin (* 2 u))) (* (sin u) (sin (* 2 v)))))
z (fn [u v] (* 3 (cos v) (cos v)))
u (range (/ Π -2) (+ (/ Π 2) i) i)
v (range 0 (+ Π i) i)]
{:polygons (vec
(mesh
(dec (count u))
(dec (count v))))
:points (vec
(for [v' v
u' u
:let [d (d u' v')]]
(point
(/ (x u' v') d)
(/ (y u' v') d)
(/ (z u' v') d))))}))
(def rect
(let [[width height] (canvas-size)]
{:x 0 :y 0 :w width :h height}))
(def dimensions
(mapv rect [:w :h]))
(defn inflate [shape multiplier]
((transform-shape (scale multiplier)) shape))
(def divisions
(js/parseInt (value-of :divisions 40)))
(def alpha
(js/parseFloat (value-of :alpha 1)))
(def shape
(->
(make-boys-surface divisions alpha)
(center-at-origin)
(inflate (js/parseFloat (value-of :scale 1)))))
(def style
(keyword (value-of :style :transparent)))
(def color
(coerce (value-of :color :white)))
(def color-fn
(condp = style
:transparent (wireframe color style)
:translucent (wireframe color style)
:opaque (wireframe color style)
:shaded (solid color)))
(defn render-shape
"Draws the shape at the given state of the world (the x,y,z rotation angles)"
[[x y z]]
(-> ctx
(fill-style "rgba(255,255,255,0.75")
(fill-rect rect))
((->canvas ctx)
(partial draw-solid
{:style style
:focal-length 3
:color-fn color-fn
:shape shape
:transform (combine
(rotate :x (degrees->radians x))
(rotate :y (degrees->radians y))
(rotate :z (degrees->radians z))
(translate 0 0 10))})
dimensions))
(defn update-state
"Increment/decrement the rotation angles around the x,y and z axes"
[event [x y z]]
[(+ x 0.3) (- y 0.7) (+ z 0.5)])
(->
ctx
(fill-style :white)
(fill-rect rect))
(show canvas)
(big-bang
:initial-state [0 0 0]
:on-tick update-state
:to-draw render-shape)

$\mathbb{R}^3$ Parametric equations

$x = {\sqrt 2 \cos^2 v \cos (2u) + \cos u \sin (2v) \over 2 - \alpha \sqrt 2 \sin (3u) \sin (2v)}$

$y = {\sqrt 2 \cos^2 v \sin (2u) - \sin u \sin (2v) \over 2 - \alpha \sqrt 2 \sin (3u) \sin (2v)}$

$z = {3 \cos^2 \over 2 - \alpha \sqrt 2 \sin (3u) \sin (2v)}$

for $u \in [-\pi /2, \pi/2]$ and $v \in [0, \pi]$

when $\alpha = 1$, this describes the Boy surface, when $\alpha = 0$, this describes the Roman surface.

By varying $\alpha$ between 0 and 1, a smooth deformation between the Roman surface and Boy surface is demonstrated. Example (at $\alpha = 0.75$)

Try translucent, opaque or shaded styles (these are all a bit slower due to the extra hidden face removal calculations required)


Built using the wireframes library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment