Square limit

Demo version of the square_limit topic notebook in examples/topics/geometry .

Most examples work across multiple plotting backends, this example is also available for:

In [1]:
import holoviews as hv
import numpy as np
from matplotlib.path import Path
from matplotlib.transforms import Affine2D
hv.extension('matplotlib')
%output fig='svg'

Declaring data and transforms

In [2]:
spline=[(0.0,1.0),(0.08,0.98),(0.22,0.82),(0.29,0.72),(0.29,0.72),(0.3,0.64),(0.29,0.57),(0.3,0.5),
(0.3,0.5),(0.34,0.4),(0.43,0.32),(0.5,0.26),(0.5,0.26),(0.58,0.21),(0.66,0.22),(0.76,0.2),(0.76,0.2),
(0.82,0.12),(0.94,0.05),(1.0,0.0),(1.0,0.0),(0.9,0.03),(0.81,0.04),(0.76,0.05),(0.76,0.05),(0.69,0.04),
(0.62,0.04),(0.55,0.04),(0.55,0.04),(0.49,0.1),(0.4,0.17),(0.35,0.2),(0.35,0.2),(0.29,0.24),(0.19,0.28),
(0.14,0.31),(0.14,0.31),(0.09,0.35),(-0.03,0.43),(-0.05,0.72),(-0.05,0.72),(-0.04,0.82),(-0.02,0.95),(0.0,1.0),
(0.1,0.85),(0.14,0.82),(0.18,0.78),(0.18,0.75),(0.18,0.75),(0.16,0.74),(0.14,0.73),(0.12,0.73),(0.12,0.73),
(0.11,0.77),(0.11,0.81),(0.1,0.85),(0.05,0.82),(0.1,0.8),(0.08,0.74),(0.09,0.7),(0.09,0.7),(0.07,0.68),
(0.06,0.66),(0.04,0.67),(0.04,0.67),(0.04,0.73),(0.04,0.81),(0.05,0.82),(0.11,0.7),(0.16,0.56),(0.24,0.39),
(0.3,0.34),(0.3,0.34),(0.41,0.22),(0.62,0.16),(0.8,0.08),(0.23,0.8),(0.35,0.8),(0.44,0.78),(0.5,0.75),
(0.5,0.75),(0.5,0.67),(0.5,0.59),(0.5,0.51),(0.5,0.51),(0.46,0.47),(0.42,0.43),(0.38,0.39),(0.29,0.71),
(0.36,0.74),(0.43,0.73),(0.48,0.69),(0.34,0.61),(0.38,0.66),(0.44,0.64),(0.48,0.63),(0.34,0.51),(0.38,0.56),
(0.41,0.58),(0.48,0.57),(0.45,0.42),(0.46,0.4),(0.47,0.39),(0.48,0.39),(0.42,0.39),(0.43,0.36),(0.46,0.32),
(0.48,0.33),(0.25,0.26),(0.17,0.17),(0.08,0.09),(0.0,0.01),(0.0,0.01),(-0.08,0.09),(-0.17,0.18),(-0.25,0.26),
(-0.25,0.26),(-0.2,0.37),(-0.11,0.47),(-0.03,0.57),(-0.17,0.26),(-0.13,0.34),(-0.08,0.4),(-0.01,0.44),
(-0.12,0.21),(-0.07,0.29),(-0.02,0.34),(0.05,0.4),(-0.06,0.14),(-0.03,0.23),(0.03,0.28),(0.1,0.34),(-0.02,0.08),
(0.02,0.16),(0.09,0.23),(0.16,0.3)]

rotT = Affine2D().rotate_deg(90).translate(1, 0)
rot45T = Affine2D().rotate_deg(45).scale(1. / np.sqrt(2.), 1. / np.sqrt(2.)).translate(1 / 2., 1 / 2.)
flipT = Affine2D().scale(-1, 1).translate(1, 0)

def combine(obj):
    "Collapses overlays of Splines to allow transforms of compositions"
    if not isinstance(obj, hv.Overlay): return obj
    return hv.Spline((np.vstack([el.data[0] for el in obj.values()]),
                      np.hstack([el.data[1] for el in obj.values()])))
    
def T(spline, transform):
    "Apply a transform to a spline or overlay of splines"
    spline = combine(spline)        
    result = Path(spline.data[0], codes=spline.data[1]).transformed(transform)
    return hv.Spline((result.vertices, result.codes))

def beside(spline1, spline2, n=1, m=1):
    den = float(n + m)
    t1 = Affine2D().scale(n / den, 1)
    t2 = Affine2D().scale(m / den, 1).translate(n / den, 0)
    return combine(T(spline1, t1) * T(spline2, t2))

def above(spline1, spline2, n=1, m=1):
    den = float(n + m)
    t1 = Affine2D().scale(1, n / den).translate(0, m / den)
    t2 = Affine2D().scale(1, m / den)
    return combine(T(spline1, t1) * T(spline2, t2))

def nonet(p, q, r, s, t, u, v, w, x):
    return above(beside(p, beside(q, r), 1, 2),
                 above(beside(s, beside(t, u), 1, 2),
                       beside(v, beside(w, x), 1, 2)), 1, 2)

def quartet(p, q, r, s):
    return above(beside(p, q), beside(r, s))

def side(n,t):
    if n == 0: 
        return hv.Spline(([(np.nan, np.nan)],[1]))
    else: 
        return quartet(side(n-1,t), side(n-1,t), rot(t), t)

def corner(n,u,t):
    if n == 0:
        return hv.Spline(([(np.nan, np.nan)],[1]))
    else:
        return quartet(corner(n-1,u,t), side(n-1,t), rot(side(n-1,t)), u)
    
def squarelimit(n,u,t):
    return nonet(corner(n,u,t), side(n,t), rot(rot(rot(corner(n,u,t)))),
                 rot(side(n,t)), u, rot(rot(rot(side(n,t)))), 
                 rot(corner(n,u,t)), rot(rot(side(n,t))), rot(rot(corner(n,u,t))))

def rot(el):        return T(el,rotT)
def rot45(el):      return T(el, rot45T)
def flip(el):       return T(el, flipT)

Plot

In [3]:
%%opts Spline [xaxis=None yaxis=None aspect='equal' bgcolor='white'] (linewidth=0.8)
%%output size=250

fish = hv.Spline((spline, [1,4,4,4]*34)) # Cubic splines
smallfish = flip(rot45(fish))
t =  fish *  smallfish * rot(rot(rot(smallfish)))
u = smallfish * rot(smallfish) * rot(rot(smallfish)) * rot(rot(rot(smallfish)))
squarelimit(3,u,t)
Out[3]: