Created
July 26, 2017 05:06
-
-
Save pirrmann/ccdbcd17380e1688842851a8714fa0c7 to your computer and use it in GitHub Desktop.
Escher's fish using FSketch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#r "../packages/FSketch/lib/net452/FSketch.dll" | |
#r "../packages/FSketch.Svg/lib/net452/FSketch.Svg.dll" | |
#r "../packages/FSketch.Drawing/lib/net452/FSketch.Drawing.dll" | |
open FSketch | |
open FSketch.Dsl | |
open FSketch.Builder | |
#r "../packages/FSketch.Winforms/lib/net452/FSketch.Winforms.dll" | |
fsi.AddPrintTransformer(fun (shapes:FSketch.Shapes) -> | |
shapes |> FSketch.Frame.FromShapes |> FSketch.Winforms.WinformsDrawer.Draw |> ignore | |
null) | |
let fishPaths = | |
[Path | |
{SubPaths = | |
[{Start = Vector (0.0,0.0) | |
Parts = | |
[Bezier (Vector (29.0, 28.0), Vector (8.0, 2.0), Vector (22.0, 18.0)) | |
Bezier (Vector (1.0, 22.0), Vector (1.0, 8.0), Vector (0.0, 15.0)) | |
Bezier (Vector (20.0, 24.0), Vector (4.0, 10.0), Vector (13.0, 18.0)) | |
Bezier (Vector (26.0, 6.0), Vector (8.0, 5.0), Vector (16.0, 4.0)) | |
Bezier (Vector (24.0, 20.0), Vector (6.0, 8.0), Vector (18.0, 15.0)) | |
Bezier (Vector (-24.0, -5.0), Vector (-10.0, -3.0), Vector (-19.0, -4.0)) | |
Bezier (Vector (-21.0, 1.0), Vector (-7.0, 1.0), Vector (-14.0, 1.0)) | |
Bezier (Vector (-20.0, -16.0), Vector (-6.0, -6.0), Vector(-15.0, -13.0)) | |
Bezier (Vector (-21.0, -11.0), Vector (-6.0, -4.0), Vector (-16.0, -8.0)) | |
Bezier (Vector (-19.0, -41.0), Vector (-5.0, -4.0), Vector (-17.0, -12.0)) | |
Bezier (Vector (5.0, -28.0), Vector (1.0, -10.0), Vector (3.0, -23.0))] | |
Closed = true}]} | |
Path | |
{SubPaths = | |
[{Start = Vector (10.0,15.0) | |
Parts = | |
[Bezier (Vector (8.0,10.0),Vector (4.0,3.0),Vector (8.0,7.0)) | |
Bezier (Vector (-6.0,2.0),Vector (-2.0,1.0),Vector (-4.0,2.0)) | |
Bezier (Vector (-2.0,-12.0),Vector (-1.0,-4.0),Vector (-1.0,-8.0))] | |
Closed = true}]} | |
Path | |
{SubPaths = | |
[{Start = Vector (5.0,18.0) | |
Parts = | |
[Bezier (Vector (4.0,12.0),Vector (5.0,2.0),Vector (3.0,8.0)) | |
Bezier (Vector (-5.0,3.0),Vector (-2.0,2.0),Vector (-3.0,4.0)) | |
Bezier (Vector (1.0,-15.0),Vector (0.0,-6.0),Vector (0.0,-14.0))] | |
Closed = true}]} | |
Path | |
{SubPaths = | |
[{Start = Vector (11.0,30.0) | |
Parts = | |
[Bezier (Vector (19.0,36.0),Vector (5.0,14.0),Vector (13.0,31.0)) | |
Bezier (Vector (50.0,26.0),Vector (11.0,12.0),Vector (32.0,18.0))] | |
Closed = false}]} | |
Path | |
{SubPaths = | |
[{Start = Vector (23.0,20.0) | |
Parts = | |
[Bezier (Vector (27.0,5.0),Vector (12.0,0.0),Vector (21.0,2.0)) | |
Bezier (Vector (0.0,24.0),Vector (0.0,8.0),Vector (0.0,16.0)) | |
Bezier (Vector (-12.0,12.0),Vector (-4.0,4.0),Vector (-8.0,8.0))] | |
Closed = false}]} | |
Path | |
{SubPaths = | |
[{Start = Vector (29.0,29.0) | |
Parts = | |
[Bezier (Vector (19.0,2.0),Vector (7.0,-3.0),Vector (14.0,-2.0))] | |
Closed = false}]} | |
Path | |
{SubPaths = | |
[{Start = Vector (34.0,39.0) | |
Parts = | |
[Bezier (Vector (14.0,-2.0),Vector (4.0,-5.0),Vector (10.0,-3.0))] | |
Closed = false}]} | |
Path | |
{SubPaths = | |
[{Start = Vector (45.0,58.0) | |
Parts = | |
[Bezier (Vector (3.0,3.0),Vector (1.0,2.0),Vector (2.0,3.0))] | |
Closed = false}]} | |
Path | |
{SubPaths = | |
[{Start = Vector (42.0,61.0) | |
Parts = | |
[Bezier (Vector (6.0,6.0),Vector (1.0,3.0),Vector (4.0,7.0))] | |
Closed = false}]} | |
Path | |
{SubPaths = | |
[{Start = Vector (25.0,74.0) | |
Parts = | |
[Bezier (Vector (-25.0,25.0),Vector (-8.0,9.0),Vector (-17.0,17.0)) | |
Bezier (Vector (-25.0,-25.0),Vector (-8.0,-8.0),Vector (-17.0,-17.0)) | |
Bezier (Vector (22.0,-31.0),Vector (5.0,-11.0),Vector (14.0,-21.0))] | |
Closed = false}]} | |
Path | |
{SubPaths = | |
[{Start = Vector (-17.0,74.0) | |
Parts = | |
[Bezier (Vector (16.0,-18.0),Vector (4.0,-8.0),Vector (9.0,-14.0))] | |
Closed = false}]} | |
Path | |
{SubPaths = | |
[{Start = Vector (-12.0,79.0) | |
Parts = | |
[Bezier (Vector (17.0,-19.0),Vector (5.0,-8.0),Vector (10.0,-13.0))] | |
Closed = false}]} | |
Path | |
{SubPaths = | |
[{Start = Vector (-6.0,86.0) | |
Parts = | |
[Bezier (Vector (16.0,-20.0),Vector (3.0,-9.0),Vector (9.0,-14.0))] | |
Closed = false}]} | |
Path | |
{SubPaths = | |
[{Start = Vector (-2.0,92.0) | |
Parts = | |
[Bezier (Vector (18.0,-22.0),Vector (4.0,-8.0),Vector (11.0,-15.0))] | |
Closed = false}]}] | |
let fish = shapes { | |
for path in fishPaths do | |
yield path |> at origin |> scaledBy 0.01 |> translatedBy (-0.5, -0.5) |> withContour { Pens.Black with Thickness = 0.01 } | |
} | |
let frame = shapes { | |
yield square 1. |> at origin |> withContour { Pens.Red with Thickness = 0.01 } | |
} | |
let (blank:Shapes) = [] | |
let f = shapes { | |
yield | |
[ | |
lineTo (0.8, 0.0) | |
lineTo (0.0, 0.1) | |
lineTo (-0.7, 0.0) | |
lineTo (0.0, 0.2) | |
lineTo (0.4, 0.0) | |
lineTo (0.0, 0.1) | |
lineTo (-0.4, 0.0) | |
lineTo (0.0, 0.4) | |
lineTo (-0.1, 0.0) | |
] |> toClosedPath |> at (-0.4, -0.4) |> withContour { Pens.Black with Thickness = 0.01 } | |
} | |
let triangle = shapes { | |
yield | |
[ | |
lineTo (1., 1.) | |
lineTo (-1., 0.) | |
lineTo (0., -1.) | |
] |> toClosedPath |> at (-0.5, -0.5) |> withContour { Pens.Black with Thickness = 0.01 } | |
} | |
let framed shape = shapes { | |
yield! frame |> at origin | |
yield! shape |> at origin | |
} | |
let rot s = shapes { | |
yield! s |> at origin |> rotatedBy (-Pi/2.) | |
} | |
let flip s = shapes { | |
yield! s |> at origin |> scaledByX -1. | |
} | |
let beside (left, right) = shapes { | |
yield! left |> at (-0.5, 0.) |> scaledByX 0.5 | |
yield! right |> at (0.5, 0.) |> scaledByX 0.5 | |
} | |
let above (top, bottom) = shapes { | |
yield! top |> at (0., -0.5) |> scaledByY 0.5 | |
yield! bottom |> at (0., 0.5) |> scaledByY 0.5 | |
} | |
let over (a, b) = shapes { | |
yield! a |> at origin | |
yield! b |> at origin | |
} | |
f |> framed | |
f |> rot |> framed | |
f |> rot |> flip |> framed | |
f |> flip |> rot |> framed | |
f |> rot |> rot |> rot |> framed | |
beside(f, rot f) |> framed | |
above(flip f, f) |> framed | |
over(f, flip f) |> framed | |
let quartet (p, q, r, s) = | |
above(beside(p, q), beside(r, s)) | |
quartet(flip(rot(rot(rot(f)))), rot(rot(rot(f))), rot(f), flip(rot(f))) | |
let rec rectri n = | |
if n < 1 then blank | |
else | |
quartet(rot triangle, rectri (n-1), rectri (n-1), rectri (n-1)) | |
rectri 4 | |
let aboveRel (top, bottom, topWeight, bottomWeight) = | |
let totalWeight = topWeight + bottomWeight | |
let topRatio = topWeight / totalWeight | |
let bottomRatio = 1. - topRatio | |
shapes { | |
yield! top |> at origin |> scaledByY topRatio |> translatedBy (0., -0.5 + topRatio / 2.) | |
yield! bottom |> at origin |> scaledByY bottomRatio |> translatedBy (0., 0.5 - bottomRatio / 2.) | |
} | |
let besideRel (left, right, leftWeight, rightWeight) = | |
let totalWeight = leftWeight + rightWeight | |
let leftRatio = leftWeight / totalWeight | |
let rightRatio = 1. - leftRatio | |
shapes { | |
yield! left |> at origin |> scaledByX leftRatio |> translatedBy (-0.5 + leftRatio / 2., 0.) | |
yield! right |> at origin |> scaledByX rightRatio |> translatedBy (0.5 - rightRatio / 2., 0.) | |
} | |
aboveRel (f, f, 2., 3.) |> framed | |
besideRel (f, f, 2., 3.) |> framed | |
let nonet (p, q, r, s, t, u, v, w, x) = | |
aboveRel( | |
besideRel(p, beside(q, r), 1., 2.), | |
above( | |
besideRel(s, beside(t, u), 1., 2.), | |
besideRel(v, beside(w, x), 1., 2.) | |
), | |
1., 2. | |
) | |
nonet (f, f , f, | |
f, blank, f, | |
f, f , f) | |
fish |> framed | |
over(fish, rot(rot(fish))) |> framed | |
let rot45 s = shapes { | |
yield! s |> at origin |> rotatedBy (-Pi/4.) |> scaledBy (sqrt 2. / 2.) |> translatedBy (0., -0.5) | |
} | |
triangle |> framed | |
rot45(triangle) |> framed | |
let smallFish = flip(rot45(fish)) | |
smallFish |> framed | |
let t = (over(fish, over(smallFish, rot(rot(rot(smallFish)))))) | |
let u = over(over(over(smallFish, rot(smallFish)), rot(rot(smallFish))), rot(rot(rot(smallFish)))) | |
let rec side n = | |
if n < 1 then blank | |
else | |
quartet(side(n-1), side(n-1), rot(t), t) | |
side 2 | |
let rec corner n = | |
if n < 1 then blank | |
else | |
quartet(corner(n-1), side(n-1), rot(side(n-1)), u) | |
corner 2 | |
let 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)) | |
) | |
squarelimit 3 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment