This commit is contained in:
jverzani
2022-05-24 13:51:49 -04:00
parent 5c0fd1b6fe
commit 244f492f9e
240 changed files with 65211 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
[deps]
AbstractPlotting = "537997a7-5e4e-5d89-9595-2241ea00577e"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008"

15
CwJ/makie-demos/README.md Normal file
View File

@@ -0,0 +1,15 @@
# Demos
A collection of little demos made using Makie graphics, which allows interactivity through the dragging of points or the use of simple controls.
* `tangent-line`: see how the slope of secant line converges to the slope of the tangent line as `h` goes to 0
* `optimization`: Identify the optimal crossing point to minimize time when there are different velocities north and south of the x axis.
* `inscribed-area`: Identify the maximal inscribed rectangle
* `integration`: Compare visually the left Riemann approximation, the trapezoid method, and Simpson's method for different values of `n`.
* `spirograph`: adjust parameters for the plotting of spirograph(https://en.wikipedia.org/wiki/Spirograph) patterns.
* `bezier`: create Bezier curves by dragging control points.

112
CwJ/makie-demos/bezier.jl Normal file
View File

@@ -0,0 +1,112 @@
using AbstractPlotting
using AbstractPlotting.MakieLayout
using GLMakie
using LinearAlgebra
function bezier()
descr = """
Bezier Curves: B(t) = ∑(binomial(n,i) * tⁱ * (1-t)⁽ⁿ⁻¹⁾ * Pᵢ)
"""
## From http://juliaplots.org/MakieReferenceImages/gallery//edit_polygon/index.html:
function add_move!(scene, points, pplot)
idx = Ref(0); dragstart = Ref(false); startpos = Base.RefValue(Point2f0(0))
on(events(scene).mousedrag) do drag
if ispressed(scene, Mouse.left)
if drag == Mouse.down
plot, _idx = mouse_selection(scene)
if plot == pplot
idx[] = _idx; dragstart[] = true
startpos[] = to_world(scene, Point2f0(scene.events.mouseposition[]))
end
elseif drag == Mouse.pressed && dragstart[] && checkbounds(Bool, points[], idx[])
pos = to_world(scene, Point2f0(scene.events.mouseposition[]))
# very wierd, but we work with components
# not vector
z = zero(eltype(pos))
x,y = pos
ptidx = idx[]
x = clamp(x, -1, 1)
y = clamp(y, -1, 1)
points[][idx[]] = [x,y]
points[] = points[]
end
else
dragstart[] = false
end
return
end
end
upperpoints = Point2f0[(0,0), (5, 0), (5, 5), (0,5)]
lowerpoints = (Point2f0[(0,0), (5, 0), (5, -5), (0,-5)])
points = Node(Point2f0[(1, 4), (3, 0), (4,-4.0)])
# where we lay our scene:
scene, layout = layoutscene()
layout.halign = :left
layout.valign = :top
p = layout[1, 1:2] = LScene(scene)
rowsize!(layout, 1, Auto(1))
colsize!(layout, 1, Auto(1))
colsize!(layout, 2, Auto(1))
npts = layout[2,1:2] = LSlider(scene, range=3:12, startvalue=4)
layout[3,1:2] = LText(scene, chomp(descr))
# points = Node(Point2f0[(-1/2, -1/2),
# (-1/2, 1/2),
# (1/2, 1/2),
# (1/2, -1/2)])
#npts = 6
#ts = range(3pi/2, -pi/2, length=npts+2)
#points = Node(Point2f0[(cos(t),sin(t)) for t in ts[2:end-1]])
points = lift(npts.value) do val
ts = range(3pi/2, -pi/2, length=val+2)
Point2f0[(cos(t),sin(t)) for t in ts[2:end-1]]
end
bcurve = lift(points) do pts
n = length(pts) - 1
B = t -> begin
tot = 0.0
for (i, Pᵢ) in enumerate(pts)
i = i - 1
tot += binomial(n, i) * t^i * (1-t)^(n-i) * Pᵢ
end
tot
end
ts = range(0, 1, length=200)
Point2f0[B(t) for t in ts]
end
lines!(p.scene, bcurve, strokecolor=:black, strokewidth=15)
lines!(p.scene, points, strokecolor=:gray90, strokewidth=5, linestyle=:dash)
scatter!(p.scene, points)
xlims!(p.scene, (-1,1))
ylims!(p.scene, (-1,1))
add_move!(p.scene, points, p.scene[end])
scene
end

View File

@@ -0,0 +1,128 @@
using AbstractPlotting
using AbstractPlotting.MakieLayout
using GLMakie
using Roots
## Assumes f(a), f(b) are zero
## only 1 or 2 solutions for f(x) = f(c) for any c in [a,b]
f(x) = 1 - x^4
a = -1
b = 1
descr = """
Adjust the point (c, f(c)) with c > 0 to find the inscribed rectangle with maximal area
"""
function _inscribed_area(c)
zs = fzeros(u -> f(u) - f(c), a, b)
length(zs) <= 1 ? 0 : abs(zs[1] - zs[2]) * f(c)
end
D(f) = c -> (f(c + 1e-4) - f(c))/1e-4
function answer()
h = 1e-4
zs = fzeros(D(_inscribed_area), 0, b-h)
a,i = findmax(_inscribed_area.(zs))
a
end
Answer = answer()
## From http://juliaplots.org/MakieReferenceImages/gallery//edit_polygon/index.html:
function add_move!(scene, points, pplot)
idx = Ref(0); dragstart = Ref(false); startpos = Base.RefValue(Point2f0(0))
on(events(scene).mousedrag) do drag
if ispressed(scene, Mouse.left)
if drag == Mouse.down
plot, _idx = mouse_selection(scene)
if plot == pplot
idx[] = _idx; dragstart[] = true
startpos[] = to_world(scene, Point2f0(scene.events.mouseposition[]))
end
elseif drag == Mouse.pressed && dragstart[] && checkbounds(Bool, points[], idx[])
if idx[] == 3
pos = to_world(scene, Point2f0(scene.events.mouseposition[]))
# we work with components not vector
x,y = pos
c = clamp(x, a, b)
zs = fzeros(u -> f(u) - f(c), a , b)
if length(zs) == 1
c = c = first(zs)
else
c, c = zs
end
points[][1] = [c, 0]
points[][2] = [c, f(c)]
points[][3] = [c, f(c)]
points[][4] = [c, 0]
points[] = points[]
end
end
else
dragstart[] = false
end
return
end
end
c = b/2
c = first(fzeros(u -> f(u) - f(c), a , b))
area = round(abs(c - c) * f(c), digits=4)
points = Node(Point2f0[(c,0), (c, f(c)), (c, f(c)), (c, 0)])
# where we lay our scene:
scene, layout = layoutscene()
layout.halign = :left
layout.valign = :top
p = layout[0,0] = LScene(scene)
colsize!(layout, 1, Auto(1))
rowsize!(layout, 1, Auto(1))
label = layout[end, 1] = LText(scene, "Area = $area")
layout[end+1,1] = LText(scene, chomp(descr))
polycolor = lift(points) do pts
c = pts[1][1]
c = pts[3][1]
area = round(abs(c - c)*f(c), digits=4)
lbl = "Area = $area"
label.text[] = lbl
if abs(area - Answer) <= 1e-4
:green
else
:gray75
end
end
lines!(p.scene, a..b, f, strokecolor=:red, strokewidth=15)
lines!(p.scene, a..b, zero, strokecolor=:black, strokewidth=10)
poly!(p.scene, points, color = polycolor)
scatter!(p.scene, points, color = :white, strokewidth = 10, markersize = 0.05, strokecolor = :black, raw = true)
xlims!(p.scene, (a, b))
add_move!(p.scene, points, p.scene[end])
scene

View File

@@ -0,0 +1,142 @@
using AbstractPlotting
using AbstractPlotting.MakieLayout
using GLMakie
using QuadGK
function riemann(f::Function, a::Real, b::Real, n::Int; method="right")
if method == "right"
meth = f -> (lr -> begin l,r = lr; f(r) * (r-l) end)
elseif method == "left"
meth = f -> (lr -> begin l,r = lr; f(l) * (r-l) end)
elseif method == "trapezoid"
meth = f -> (lr -> begin l,r = lr; (1/2) * (f(l) + f(r)) * (r-l) end)
elseif method == "simpsons"
meth = f -> (lr -> begin l,r=lr; (1/6) * (f(l) + 4*(f((l+r)/2)) + f(r)) * (r-l) end)
end
xs = a .+ (0:n) * (b-a)/n
sum(meth(f), zip(xs[1:end-1], xs[2:end]))
end
"""
integration(f)
Show graphically the left Riemann approximation, the trapezoid approximation, and Simpson's approximation to the integral of `f` over [-1,1].
Assumes `f` is non-negative.
"""
function integration(f=nothing)
if f == nothing
f = x -> x^2*exp(-x/3)
end
a, b = -1, 1
function left_riemann_pts(n)
xs = range(a, b, length=n+1)
pts = Point2f0[(xs[1], 0)]
for i in 1:n
xᵢ, xᵢ₊₁ = xs[i], xs[i+1]
fᵢ = f(xᵢ)
push!(pts, (xᵢ, fᵢ))
push!(pts, (xᵢ₊₁, fᵢ))
end
push!(pts, (xs[end], 0))
pts
end
function trapezoid_pts(n)
xs = range(a, b, length=n+1)
pts = Point2f0[(xs[1], 0)]
for i in 1:n
xᵢ, xᵢ₊₁ = xs[i], xs[i+1]
fᵢ = f(xᵢ)
push!(pts, (xᵢ, f(xᵢ)))
end
push!(pts, (xs[end], f(xs[end])))
push!(pts, (xs[end], 0))
pts
end
function simpsons_pts(n)
xs = range(a, b, length=n+1)
pts = Point2f0[(xs[1], 0), (xs[1], f(xs[1]))]
for i in 1:n
xi, xi1 = xs[i], xs[i+1]
m = xi/2 + xi1/2
p = x -> f(xi)*(x-m)*(x - xi1)/(xi-m)/(xi-xi1) + f(m) * (x-xi)*(x-xi1)/(m-xi)/(m-xi1) + f(xi1) * (x-xi)*(x-m) / (xi1-xi) / (xi1-m)
xs = range(xi, xi1, length=10)
for j in 2:10
x = xs[j]
push!(pts, (x, p(x)))
end
end
push!(pts, (xs[end], 0))
pts
end
# where we lay our scene:
scene, layout = layoutscene()
layout.halign = :left
layout.valign = :top
p1 = layout[1,1] = LScene(scene)
p2 = layout[1,2] = LScene(scene)
p3 = layout[1,3] = LScene(scene)
n = layout[2,1] = LSlider(scene, range=2:25, startvalue=2)
output = layout[2, 2:3] = LText(scene, "...")
lpts = lift(n.value) do n
left_riemann_pts(n)
end
poly!(p1.scene, lpts, color=:gray75)
lines!(p1.scene, a..b, f, color=:black, strokewidth=10)
title(p1.scene, "Left Riemann")
tpts = lift(n.value) do n
trapezoid_pts(n)
end
poly!(p2.scene, tpts, color=:gray75)
lines!(p2.scene, a..b, f, color=:black, strokewidth=10)
title(p2.scene, "Trapezoid")
spts = lift(n.value) do n
simpsons_pts(n)
end
poly!(p3.scene, spts, color=:gray75)
lines!(p3.scene, a..b, f, color=:black, strokewidth=10)
title(p3.scene, "Simpson's")
on(n.value) do n
actual,err = quadgk(f, -1, 1)
lrr = riemann(f, -1, 1, n, method="left")
trap = riemann(f, -1, 1, n, method="trapezoid")
simp = riemann(f, -1, 1, n, method="simpsons")
Δleft = round(abs(lrr - actual), digits=8)
Δtrap = round(abs(trap - actual), digits=8)
Δsimp = round(abs(simp - actual), digits=8)
txt = "Riemann: $Δleft, Trapezoid: $Δtrap, Simpson's: $Δsimp"
output.text[] = txt
end
n.value[] = n.value[]
scene
end

View File

@@ -0,0 +1,151 @@
using AbstractPlotting
using AbstractPlotting.MakieLayout
using GLMakie
using LinearAlgebra
using Roots
using ForwardDiff
D(f) = x -> ForwardDiff.derivative(f, float(x))
descr = """
An old optimization problem is to find the shortest distance between two
points when the rate of travel differs due to some medium (darker means slower).
In this example, the relative rate can be adjusted (with the slider), and the
various points (by clicking on and dragging a point). From there, the
user can adjust the crossing point to identify the time.
"""
## From http://juliaplots.org/MakieReferenceImages/gallery//edit_polygon/index.html:
function add_move!(scene, points, pplot)
idx = Ref(0); dragstart = Ref(false); startpos = Base.RefValue(Point2f0(0))
on(events(scene).mousedrag) do drag
if ispressed(scene, Mouse.left)
if drag == Mouse.down
plot, _idx = mouse_selection(scene)
if plot == pplot
idx[] = _idx; dragstart[] = true
startpos[] = to_world(scene, Point2f0(scene.events.mouseposition[]))
end
elseif drag == Mouse.pressed && dragstart[] && checkbounds(Bool, points[], idx[])
pos = to_world(scene, Point2f0(scene.events.mouseposition[]))
# very wierd, but we work with components
# not vector
z = zero(eltype(pos))
x,y = pos
ptidx = idx[]
x = clamp(x, 0, 5)
if ptidx == 1
y = clamp(y, z, 5)
elseif ptidx == 2
y = z
elseif ptidx == 3
y = clamp(y, -5, z)
end
points[][idx[]] = [x,y]
points[] = points[]
end
else
dragstart[] = false
end
return
end
end
upperpoints = Point2f0[(0,0), (5, 0), (5, 5), (0,5)]
lowerpoints = (Point2f0[(0,0), (5, 0), (5, -5), (0,-5)])
points = Node(Point2f0[(1, 4), (3, 0), (4,-4.0)])
# where we lay our scene:
scene, layout = layoutscene()
layout.halign = :left
layout.valign = :top
p = layout[1:3, 1] = LScene(scene)
rowsize!(layout, 1, Auto(1))
colsize!(layout, 1, Auto(1))
flayout = GridLayout()
layout[1,2] = flayout
flayout[1,1] = LText(scene, chomp(descr))
details = flayout[2, 1] = LText(scene, "...")
λᵣ = flayout[3,1] = LText(scene, "λ = v₁/v₂ = 1")
λ = flayout[4,1] = LSlider(scene, range = -3:0.2:3, startvalue = 0.0)
tm = lift(λ.value, points) do λ, pts
x0,y0 = pts[1]
x, y = pts[2]
x1, y1 = pts[3]
v1 = 1
v2 = v1/2.0^λ
t(x) = sqrt((x-x0)^2 + y0^2)/v1 + sqrt((x1-x)^2 + y1^2)/v2
val = t(x)
details.text[] = "Time: $(round(val, digits=2)) units"
val
end
a = lift(λ.value, points) do λ, pts
x0,y0 = pts[1]
x1, y1 = pts[3]
v1 = 1
v2 = v1/2.0^λ
t(x) = sqrt((x-x0)^2 + y0^2)/v1 + sqrt((x1-x)^2 + y1^2)/v2
x = fzero(D(t), x0, x1)
t(x)
end
λcolor = lift(λ.value) do val
# val = v1/v2 ∈ [1/8, 8]
n = floor(Int, 50 - val/3 * 25)
Symbol("gray" * string(n))
end
linecolor = lift(a) do val
abs(val - tm[]) <= 1e-2 ? :green : :white
end
on(λ.value) do val
v = round(2.0^(val), digits=2)
txt = "λ = v₁/v₂ = $v"
λᵣ.text[] = txt
end
poly!(p.scene, upperpoints, color = :gray50) # neutral color
poly!(p.scene, lowerpoints, color = λcolor) # color depends on λ
lines!(p.scene, Point2f0[(0,0), (5, 0)], color=:black, strokewidth=5, strokecolor=:black, raw=true)
lines!(p.scene, points, color = linecolor, strokewidth = 10, markersize = 0.05, strokecolor = :black, raw = true)
scatter!(p.scene, points, color = linecolor, strokewidth = 10, markersize = 0.05, strokecolor = :black, raw = true)
xlims!(p.scene, (0,5))
ylims!(p.scene, (-5,5))
add_move!(p.scene, points, p.scene[end])
scene

View File

@@ -0,0 +1,49 @@
using AbstractPlotting
using AbstractPlotting.MakieLayout
using GLMakie
# GUI for spirograph
# https://en.wikipedia.org/wiki/Spirograph
function x(t; R=1, k=1/4, l=1/4)
R*[(1-k)*cos(t) + l*k*cos((1-k)/k*t), (1-k)*sin(t) - l*k*sin((1-k)/k*t)]
end
# where we lay our scene:
scene, layout = layoutscene()
flyt = GridLayout()
flyt.halign[] = :left # fails?
flyt.valign[] = :top
layout[1,1] = flyt
p = layout[1,2] = LAxis(scene)
rowsize!(layout, 1, Relative(1))
colsize!(layout, 2, Relative(2/3))
flyt[1,1] = LText(scene, "t")
ts = flyt[1,2] = LSlider(scene, range = 2pi:pi/8:40pi)
flyt[2, 1] = LText(scene, "k = r/R")
k = flyt[2,2] = LSlider(scene, range = 0.01:0.01:1.0, startvalue=1/4)
flyt[3,1] = LText(scene, "l=ρ/r")
l = flyt[3,2] = LSlider(scene, range = 0.01:0.01:1.0, startvalue=1/4)
data = lift(ts.value, k.value, l.value) do ts,k,l
ts = range(0, ts, length=1000)
xys = Point2f0.(x.(ts, R=1, k=k, l=l))
end
lines!(p, data)
xlims!(p, (-1, 1))
ylims!(p, (-1, 1))
scene

View File

@@ -0,0 +1,102 @@
using AbstractPlotting
using AbstractPlotting.MakieLayout
using GLMakie
using ForwardDiff
Base.adjoint(f::Function) = x -> ForwardDiff.derivative(f, float(x))
function tangent_line(f=nothing, a=0, b=pi)
if f == nothing
f = x -> sin(x)
end
descr = """
The tangent line has a slope approximated by the slope of secant lines.
This demo allows the points c and c+h to be adjusted to see the two lines"""
## From http://juliaplots.org/MakieReferenceImages/gallery//edit_polygon/index.html:
function add_move!(scene, points, pplot)
idx = Ref(0); dragstart = Ref(false); startpos = Base.RefValue(Point2f0(0))
on(events(scene).mousedrag) do drag
if ispressed(scene, Mouse.left)
if drag == Mouse.down
plot, _idx = mouse_selection(scene)
if plot == pplot
idx[] = _idx; dragstart[] = true
startpos[] = to_world(scene, Point2f0(scene.events.mouseposition[]))
end
elseif drag == Mouse.pressed && dragstart[] && checkbounds(Bool, points[], idx[])
pos = to_world(scene, Point2f0(scene.events.mouseposition[]))
# we work with components not vector
x,y = pos
x = clamp(x, a, b)
y = f(x)
points[][idx[]] = [x,y]
points[] = points[]
end
else
dragstart[] = false
end
return
end
end
c, h = pi/4, .5
points = Node(Point2f0[(c, f(c)), (c+h, f(c+h))])
# where we lay our scene:
scene, layout = layoutscene()
layout.halign = :left
layout.valign = :top
p = layout[1,1:2] = LScene(scene)
rowsize!(layout, 1, Auto(1))
colsize!(layout, 1, Auto(1))
layout[2,1:2] = LText(scene, descr)
secline = lift(points) do pts
c, ch = pts
x0, y0 = c
x1, y1 = ch
m = (y1 - y0)/(x1 - x0)
sl = x -> y0 + m * (x - x0)
Point2f0[(a, sl(a)), (b, sl(b))]
end
tangentline = lift(points) do pts
c, ch = pts
x0, y0 = c
m = f'(x0)
tl = x -> y0 + m * (x - x0)
Point2f0[(a, tl(a)), (b, tl(b))]
end
lines!(p.scene, a..b, f, strokecolor=:red, strokewidth=15)
lines!(p.scene, secline, color = :blue, strokewidth = 10, raw=true)
lines!(p.scene, tangentline, color = :red, strokewidth = 10, raw=true)
scatter!(p.scene, points, color = :white, strokewidth = 10, markersize = 0.05, strokecolor = :black, raw = true)
xlims!(p.scene, (a, b))
#ylims!(p.scene, (0, 1.5))
add_move!(p.scene, points, p.scene[end])
scene
end
tangent_line()