Skip to content

Instantly share code, notes, and snippets.

@pirrmann
Created July 26, 2017 05:06
Show Gist options
  • Save pirrmann/ccdbcd17380e1688842851a8714fa0c7 to your computer and use it in GitHub Desktop.
Save pirrmann/ccdbcd17380e1688842851a8714fa0c7 to your computer and use it in GitHub Desktop.
Escher's fish using FSketch
#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