Skip to content

Instantly share code, notes, and snippets.

@juliaogris
Last active November 5, 2024 03:31
Show Gist options
  • Save juliaogris/4802be4a9173dc1406725d8cee1c3630 to your computer and use it in GitHub Desktop.
Save juliaogris/4802be4a9173dc1406725d8cee1c3630 to your computer and use it in GitHub Desktop.
HOME_XMIN := -1.1
HOME_XMAX := 5.9
HOME_YMIN := -3.5
HOME_YMAX := 3.5
xMin:num
yMin:num
xMax:num
yMax:num
xExtend:num
yExtend:num
zooming:bool
isDrag := false
dragIndex := -1
dragIndex2 := -1
tangentColors := ["blue" "green" "orange" "purple"]
tangentPoints := [ // x1 y1 x2 y2 m c- full colored circles on lines
[0 -1.3 4 -1.3]
[0 -1.6 4 -1.6]
[0 -1.9 4 -1.9]
[0 -2.2 4 -2.2]
]
tangentTargets := [ // outline only circles on sin curve
[0 (sin 0) 1]
[2*pi/6 (sin 2*pi/6) 1/2]
[3*pi/6 (sin 3*pi/6) 0]
[5*pi/6 (sin 5*pi/6) -(sqrt 3)/2]
]
NUM_TANGENTS := len tangentPoints
tangentDone := [false] * NUM_TANGENTS
func drawContent
drawSin
drawTangents false
if (allTangentsDone)
drawSolution
exit 0
end
end
func drawSolution
print "✅ You have matched all the tangents!"
sleep 2
clear
drawGraphPaper
drawSin
drawTangents true
print "✅ You have matched all the tangents!"
print " Plotting slopes."
drawSlopes
print " Plotting <??> curve."
sleep 2
drawCos
end
func drawSlopes
for i := range NUM_TANGENTS
gmove tangentTargets[i][0] tangentTargets[i][2]
color tangentColors[i]
circle 1.5
sleep 1
end
end
func drawCos
color "grey"
width 0.1
step := xstep
gmove xMin (cos xMin)
for x := range xMin xMax+step step
gline x (cos x)
sleep 0.01
end
end
on down x:num y:num
calcDragIndices (gx x) (gy y)
if dragIndex >= 0
return
end
onDown x y
end
on move x:num y:num
if dragIndex >= 0
tangentPoints[dragIndex][dragIndex2] = gx x
tangentPoints[dragIndex][dragIndex2 + 1] = gy y
draw
return
end
onMove x y
end
on up x:num y:num
dragIndex = -1
dragIndex2 = -1
onUp x y
draw
end
func drawSin
color "red"
gmove xMin (sin xMin)
for x := range xMin xMax (xstep)
y := sin x
gline x y
end
for x := range -pi/2 7 pi/6
y := sin x
gmove x y
circle 0.5
end
end
func drawTangents allDone:bool
cls
for i := range NUM_TANGENTS
drawTangent i allDone
end
end
func drawTangent i:num allDone:bool
x1 := tangentPoints[i][0]
y1 := tangentPoints[i][1]
x2 := tangentPoints[i][2]
y2 := tangentPoints[i][3]
hue := tangentColors[i]
target := tangentTargets[i]
done := tangentDone[i]
color hue
m := (y2 - y1) / (x2 - x1)
c := y1 - m * x1
doneNow := false
if !done
doneNow = calcDone m c target
if doneNow // done for the first time
done = true
tangentDone[i] = true
end
end
if done
m = target[2]
c = target[1] - m * target[0]
else
drawLineDots x1 y1 x2 y2
end
d := done and !allDone
drawLineMC m c d
drawTarget target d
printSlope target[0] m hue done
end
func drawTarget target:[]num done:bool
gmove target[0] target[1]
if !done
fill "transparent"
circle 1.5
else
circle 0.75
font {size:3}
text "✅"
end
end
func drawLineDots x1:num y1:num x2:num y2:num
gmove x1 y1
circle 1
gmove x2 y2
circle 1
end
func drawLineMC m:num c:num thick:bool
if thick
width 0.5
else
width 0.3
end
gmove xMin m*xMin+c
gline xMax m*xMax+c
end
func printSlope x:num m:num label:string done:bool
if !zooming and !isDrag and dragIndex < 0
if done
printf "%-6s x: %5.2f slope: %5.2f ✅\n" label x m
else
printf "%-6s x: %5.2f slope: %5.2f\n" label x m
end
end
end
func calcDragIndices x:num y:num
eps := xExtend / 50 // circle radius is 1, which is evyExtend/100
for i := range NUM_TANGENTS
if (abs x-tangentPoints[i][0]) < eps and (abs y-tangentPoints[i][1]) < eps and !tangentDone[i]
dragIndex = i
dragIndex2 = 0
return
else if (abs x-tangentPoints[i][2]) < eps and (abs y-tangentPoints[i][3]) < eps and !tangentDone[i]
dragIndex = i
dragIndex2 = 2
return
end
end
dragIndex = -1
dragIndex2 = -1
end
func calcDone:bool m:num c:num target:[]num
eps := max (abs target[2]*0.1) 0.01
return (abs target[2]-m) < eps and (abs m*target[0]+c-target[1]) < yExtend / 100
end
func xstep:num
if zooming or isDrag
return (xMax - xMin) / 10
end
return (xMax - xMin) / 100
end
func allTangentsDone:bool
for d := range tangentDone
if !d
return false
end
end
return true
end
// ---------------------✂-------------------------
// generic graphing paper, setup and even handlers
xScale:num
yXcale:num
xTick:num
yTick:num
originHidden:bool
// initialize
zoomHome
cls
print "Sine derivative intuition"
print "========================"
print "Can you match the Sine tangents?"
print "Drag the colored lines to the Sine points"
print "highlighted with circles of same color."
func draw
drawGraphPaper
drawContent
end
func update xMin1:num yMin1:num xScale1:num yXcale1:num
xMin = xMin1
yMin = yMin1
xScale = xScale1
yXcale = yXcale1
// derived
xExtend = 100 / xScale
yExtend = 100 / yXcale
xMax = xMin + xExtend
yMax = yMin + yExtend
xTick = calcTick xExtend
yTick = calcTick yExtend
originHidden = xMin > 0 or xMax < 0 or yMin > 0 or yMax < 0
end
xDrag := 0
yDrag := 0
func setDragState d:bool x:num y:num
isDrag = d
xDrag = x
yDrag = y
end
func drag x:num y:num
if isDrag
update xMin+(xDrag - x)/xScale yMin+(yDrag - y)/yXcale xScale yXcale
setDragState true x y
draw
end
end
func zoomHome
homeXScale := 100 / (HOME_XMAX - HOME_XMIN)
homeYXcale := 100 / (HOME_YMAX - HOME_YMIN)
update HOME_XMIN HOME_YMIN homeXScale homeYXcale
draw
end
func zoomIn
zoom 2 0 0 //1.1 7 0.01
end
func zoomOut
zoom 1/2 0 0 // 1/1.1 7 0.01
end
func zoom f:num count:num dur:num
zooming = true
for range count-1
zoom1 f
draw
sleep dur
end
zooming = false
zoom1 f
draw
end
func zoom1 f:num
zoomxy f
end
func zoomxy f:num
xd := xExtend * (f - 1) / (2 * f)
yd := yExtend * (f - 1) / (2 * f)
update xMin+xd yMin+yd xScale*f yXcale*f
end
func zoomx f:num
xd := xExtend * (f - 1) / (2 * f)
update xMin+xd yMin xScale*f yXcale
end
func drawGraphPaper
clear
width 0.05
color "gainsboro"
drawGrid xTick/5 yTick/5
width 0.1
color "dimgrey"
drawGrid xTick yTick
width 0.2
color (hsl 0 0 20)
drawAxes
if !zooming
drawXLabels
drawYLabels
drawButtons
end
end
func drawGrid xdist:num ydist:num
start := roundUp xMin xdist
stop := xMax + xdist
for x := range start stop xdist
gv x yMin yMax
end
start = roundUp yMin ydist
stop = yMax + ydist
for y := range start stop ydist
gh xMin xMax y
end
end
func drawAxes
gh xMin xMax 0 // x-Axis
gv 0 yMin yMax // y-Axis
if !originHidden
font {baseline:"alphabetic" align:"left" size:2}
gtextnumf 0.05*xTick 0.05*yTick 0 "%v"
end
end
func drawXLabels
y := yMin // labels on very bottom if x-axis not visible
if yMin <= 0 and yMax > 0
y = 0 // x-axis visible
end
font {baseline:"alphabetic" align:"center" size:2}
start := roundUp xMin xTick
stop := xMax + xTick
tl := 0.025 * yTick
for x := range start stop xTick
gv x y-tl y+tl
// don't double label origin
if (abs x) > xTick / 2 or originHidden
gtextnum x y+2*tl x xExtend
end
end
end
func drawYLabels
font {baseline:"middle" align:"left" size:2}
x := xMin // labels on very left if y-axis not visible
if xMin <= 0 and xMax > 0
x = 0 // y-axis visible
end
start := roundUp yMin yTick
stop := yMax + yTick
tl := 0.025 * xTick
for y := range start stop yTick
gh x-tl x+tl y
// don't double label origin
if (abs y) > yTick / 2 or originHidden
gtextnum x+2*tl y y yExtend
end
end
end
func drawButtons
fill "gainsboro"
stroke "darkgrey"
width 0.3
move 93 93
rect 6 6
move 93 85
rect 6 6
move 93 79
rect 6 6
// labels
color "grey"
font {baseline:"alphabetic" align:"left" size:3}
move 94.1 94.6
text "🏠"
font {baseline:"alphabetic" align:"left" size:6}
move 94.2 86.1
text "+"
move 94.2 80.1
text "-"
end
func onDown x:num y:num
if isHome x y
zoomHome
else if isZoomIn x y
zoomIn
else if isZoomOut x y
zoomOut
else
setDragState true x y
end
end
func onMove x:num y:num
drag x y
end
func onUp _:num _:num
setDragState false 0 0
draw
end
func isHome:bool x:num y:num
return x >= 93 and x <= 99 and y >= 93 and y <= 99
end
func isZoomIn:bool x:num y:num
return x >= 93 and x <= 99 and y >= 85 and y <= 91
end
func isZoomOut:bool x:num y:num
return x >= 93 and x <= 99 and y >= 79 and y <= 85
end
func calcTick:num extend:num
d := log10 extend/10
f := d - (floor d)
if f > 0.15 and f < 0.42
return 2 * (pow 10 (round d))
else if f >= 0.42 and f < 0.5
return 5 * (pow 10 (round d))
else if f >= 0.5 and f < 0.78
return 0.5 * (pow 10 (round d))
end
return pow 10 (round d)
end
func gmove x:num y:num
move (ex x) (ey y)
end
func gline x:num y:num
line (ex x) (ey y)
end
// vertical line
func gv x:num y1:num y2:num
gmove x y1
gline x y2
end
// horizontal line
func gh x1:num x2:num y:num
gmove x1 y
gline x2 y
end
func gtextnum x:num y:num n:num extend:num
p := 0
lg := floor (log10 extend/10)
if lg < 0
p = abs (lg)
end
fstr := sprintf "%%.%0.ff" p
gtextnumf x y n fstr
end
func gtextnumf x:num y:num n:num fstr:string
gmove x y
text (sprintf fstr n)
end
// ex is the evy x coordinate (0, 100) for a given
// graphing x coordinate graphX (xMin, xMin+extend).
func ex:num graphX:num
return xScale * (graphX - xMin)
end
// ey is the evy y coordinate (0, 100) for a given
// graphing y coordinate graphY (yMin, yMin+extend).
func ey:num graphY:num
return yXcale * (graphY - yMin)
end
// gx is the grpahing x coordinate (xMin, xMin+extend)
// for a gvien evy x coordinate evyX (0, 100)
func gx:num evyX:num
return xMin + evyX / xScale
end
// gy is the grpahing y coordinate (yMin, yMin+extend)
// for a gvien evy y coordinate evyY(0, 100)
func gy:num evyY:num
return yMin + evyY / yXcale
end
func roundUp:num n:num multiple:num
r := (abs n) % multiple
if r == 0
return n
end
if n > 0
return n + multiple - r
end
return -(-n + multiple - r)
end
func log10:num n:num
return (log n) / (log 10)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment