Last active
November 5, 2024 03:31
-
-
Save juliaogris/4802be4a9173dc1406725d8cee1c3630 to your computer and use it in GitHub Desktop.
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
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