# This notebook is a semi top-down explanation. This cell needs to be # executed first so that the operators and helper functions are defined # All of this is explained in the later half of the notebook using Compose, Interact Compose.set_default_graphic_size(2inch, 2inch) points_f = [ (.1, .1), (.9, .1), (.9, .2), (.2, .2), (.2, .4), (.6, .4), (.6, .5), (.2, .5), (.2, .9), (.1, .9), (.1, .1) ] f = compose(context(), stroke("black"), line(points_f)) rot(pic) = compose(context(rotation=Rotation(-deg2rad(90))), pic) flip(pic) = compose(context(mirror=Mirror(deg2rad(90), 0.5w, 0.5h)), pic) above(m, n, p, q) = compose(context(), (context(0, 0, 1, m/(m+n)), p), (context(0, m/(m+n), 1, n/(m+n)), q)) above(p, q) = above(1, 1, p, q) beside(m, n, p, q) = compose(context(), (context(0, 0, m/(m+n), 1), p), (context(m/(m+n), 0, n/(m+n), 1), q)) beside(p, q) = beside(1, 1, p, q) over(p, q) = compose(context(), (context(), p), (context(), q)) rot45(pic) = compose(context(0, 0, 1/sqrt(2), 1/sqrt(2), rotation=Rotation(-deg2rad(45), 0w, 0h)), pic) # Utility function to zoom out and look at the context zoomout(pic) = compose(context(), (context(0.2, 0.2, 0.6, 0.6), pic), (context(0.2, 0.2, 0.6, 0.6), fill(nothing), stroke("black"), strokedash([0.5mm, 0.5mm]), polygon([(0, 0), (1, 0), (1, 1), (0, 1)]))) function read_path(p_str) tokens = [try parsefloat(x) catch symbol(x) end for x in split(p_str, r"[\s,]+")] path(tokens) end fish = compose(context(units=UnitBox(260, 260)), stroke("black"), read_path(strip(readall("fish.path")))) rotatable(pic) = @manipulate for θ=0:0.001:2π compose(context(rotation=Rotation(θ)), pic) end blank = compose(context()) fliprot45(pic) = rot45(compose(context(mirror=Mirror(deg2rad(-45))),pic)) # Hide this cell. display(MIME("text/html"), """""") f rot(f) flip(f) rot(flip(f)) fliprot45(fish) |> zoomout # zoomout shows the bounding box above(f, f) above(1, 2, f, f) beside(f, f) beside(1, 2, f, f) above(beside(f, f), f) over(f, flip(f)) fish |> zoomout rotatable(fish |> zoomout) over(fish, rot(rot(fish))) |> zoomout fish2 = fliprot45(fish) fish3 = rot(rot(rot(fish2))) t = over(fish, over(fish2, fish3)) t |> zoomout u = over(over(fish2, rot(fish2)), over(rot(rot(fish2)), rot(rot(rot(fish2))))) u |> zoomout quartet(p, q, r, s) = above(beside(p, q), beside(r, s)) quartet(f,flip(f),rot(f),f) # 2inch x 2inch canvas is no more sufficient, so let's blow it up a bit Compose.set_default_graphic_size(5inch, 5inch) quartet(u, u, u, u) |> zoomout cycle(p) = quartet(p, rot(p), rot(rot(p)), rot(rot(rot(p)))) cycle(f) nonet(p, q, r, s, t, u, v, w, x) = above(1,2,beside(1,2,p,beside(1,1,q,r)), above(1,1,beside(1,2,s,beside(1,1,t,u)), beside(1,2,v,beside(1,1,w,x)))) nonet(f, f, f, f, f, f, f, f, f) side1 = quartet(blank, blank, rot(t), t) side1 |> zoomout side2 = quartet(side1,side1,rot(t),t) side2 |> zoomout side(n) = if n == 1 side1 # basis else quartet(side(n-1),side(n-1),rot(t),t) # induction end side(3) |> zoomout # @manipulate lets us watch what happens as the levels increase @manipulate for level=slider(1:4, value=1) side(level) |> zoomout end corner1 = quartet(blank,blank,blank,u) corner1 |> zoomout corner2 = quartet(corner1,side1,rot(side1),u) corner2 |> zoomout corner(n) = n == 1 ? corner1 : quartet(corner(n-1), side(n-1), rot(side(n-1)), u) corner(3) |> zoomout # Touring the corners with a slider: @manipulate for level=slider(1:4, value=1) corner(level) |> zoomout end squarelimit(n) = nonet(corner(n), side(n), rot(rot(rot(corner(n)))), rot(side(n)), u, rot(rot(rot(side(n)))), rot(corner(n)), rot(rot(side(n))), rot(rot(corner(n)))) # We render a level-3 square limit on a 10inch x 10inch SVG for maximum awesome. draw(SVG(10inch, 10inch), squarelimit(3)) Compose.set_default_graphic_size(2inch, 2inch) # switch back to smaller output compose(context(), rectangle(), fill("tomato")) tomato_bisque = compose(context(), (context(), circle(), fill("bisque")), (context(), rectangle(), fill("tomato"))) introspect(tomato_bisque) introspect(u) points_f = [ (.1, .1), (.9, .1), (.9, .2), (.2, .2), (.2, .4), (.6, .4), (.6, .5), (.2, .5), (.2, .9), (.1, .9), (.1, .1) ] # line function takes a list of points and returns a Line form f = compose(context(), stroke("black"), line(points_f)) # rotate anti-clockwise by 90deg rot(pic) = compose(context(rotation=Rotation(-π/2)), pic) rot(f) flip(pic) = compose(context(mirror=Mirror(deg2rad(90), 0.5, 0.5)), pic) flip(f) rot45(pic) = compose(context(0, 0, 1/sqrt(2), 1/sqrt(2), rotation=Rotation(-π/4, 0, 0)), pic) rot45(f) fliprot45(pic) = rot45(compose(context(mirror=Mirror(deg2rad(-45))), pic)) fliprot45(f) |> zoomout above(m, n, p, q) = compose(context(), (context(0, 0, 1, m/(m+n)), p), (context(0, m/(m+n), 1, n/(m+n)), q)) above(p, q) = above(1, 1, p, q) above(f, f) beside(m, n, p, q) = compose(context(), (context(0, 0, m/(m+n), 1), p), (context(m/(m+n), 0, n/(m+n), 1), q)) beside(p, q) = beside(1, 1, p, q) beside(f, f) over(p, q) = compose(context(), (context(), p), (context(), q)) over(f, flip(f)) function read_path(p_str) tokens = [try parsefloat(x) catch symbol(x) end for x in split(p_str, r"[\s,]+")] path(tokens) end # The fish was drawn on a canvas of 260x260 units, we will specify this # by wrapping the path in a Unit box of this dimension fish = compose(context(units=UnitBox(260, 260)), stroke("black"), read_path(strip(readall("fish.path")))) # Utility function to zoom out and look at the context zoomout(pic) = compose(context(), (context(0.2, 0.2, 0.6, 0.6), pic), (context(0.2, 0.2, 0.6, 0.6), fill(nothing), stroke("black"), strokedash([0.5mm, 0.5mm]), polygon([(0, 0), (1, 0), (1, 1), (0, 1)]))) zoomout(fish) rotatable(pic) = @manipulate for θ=0:0.001:2π compose(context(rotation=Rotation(θ)), pic) end rotatable(fish |> zoomout)