Skip to content
Jim Pivarski edited this page Mar 15, 2015 · 1 revision
  1. summary Square on the Poincaré disk
  2. labels Example,Version1

_(This page applies only to the 1.x branch of SVGFig.)_

Square on the Poincaré disk

(Okay, so it's not actually a _square_, because the sides are not [http://en.wikipedia.org/wiki/Geodesic geodesics] on the hyperbolic plane. But to my Euclidean eyes, the middle one looks like a square!)

This is a grand example of recursion self-similarity: a tiling of the [http://en.wikipedia.org/wiki/Hyperbolic_plane hyperbolic plane], represented by the [http://en.wikipedia.org/wiki/Poincare_disk Poincaré disk] (one of M.C. Escher's favorite paradigms). The same [ClassPath Path] is drawn 5 iterations deep, transformed with the appropriate [http://en.wikipedia.org/wiki/M%C3%B6bius_transformation Möbius transformation]. This is perhaps the simplest case, in which the tiles are generated by the [http://en.wikipedia.org/wiki/Free_group free group].

Yes, I just learned a little [http://en.wikipedia.org/wiki/Riemannian_geometry Riemannian geometry] and was itching to draw this.

from svgfig import *
import math

def one(z): return (z + (1+1j)/2.) / ((1-1j)*z/2. + 1)
def two(z): return (z + -(1-1j)/2.) / (-(1+1j)*z/2. + 1)
def three(z): return (z - (1+1j)/2.) / (-(1-1j)*z/2. + 1)
def four(z): return (z + (1-1j)/2.) / ((1+1j)*z/2. + 1)

def recurse(t, direction, depth=0):
  if depth == 5: return Fig()

  if direction == one:
    return Fig(t, recurse(t, two, depth+1), recurse(t, one, depth+1), recurse(t, four, depth+1), trans=totrans(one))

  if direction == two:
    return Fig(t, recurse(t, three, depth+1), recurse(t, two, depth+1), recurse(t, one, depth+1), trans=totrans(two))

  if direction == three:
    return Fig(t, recurse(t, four, depth+1), recurse(t, three, depth+1), recurse(t, two, depth+1), trans=totrans(three))

  if direction == four:
    return Fig(t, recurse(t, one, depth+1), recurse(t, four, depth+1), recurse(t, three, depth+1), trans=totrans(four))

x = 1. - math.sqrt(0.5)
tile = Path("M -%g -%g L -%g %g L %g %g L %g -%g Z" % ((x,) * 8), fill="purple")

Fig(Ellipse(0, 0, 0, 1, 1, fill="lightgreen"), \
    tile, \
    recurse(tile, one), \
    recurse(tile, two), \
    recurse(tile, three), \
    recurse(tile, four)).SVG(window(-1.1, 1.1, -1.1, 1.1)).inkview()

Comments

  • Note that we could have loaded a <path /> from a file: we can tile the hyperbolic plane with _anything!_
  • Only the vertices are transformed, not the edges of the [ClassPath Path]. We could have replaced the [ClassPath Path] with a [ClassRect Rect]. That would have curved the edges, but it takes a long time. Perhaps the adaptive sampling algorithm could be optimized or compiled.
[http://svgfig.googlecode.com/svn/wiki/ExamplePoincare.svg http://svgfig.googlecode.com/svn/wiki/ExamplePoincare.png]
[http://svgfig.googlecode.com/svn/wiki/ExamplePoincare.svg ExamplePoincare.svg]
Clone this wiki locally