Last active
December 16, 2016 07:55
-
-
Save ctlusto/d8a7ff065ccf1c724e2d802481342f0a to your computer and use it in GitHub Desktop.
Exponential Population Growth
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Exponential Population Growth</title> | |
<link rel="stylesheet" href="scrubber.css"> | |
<link rel="stylesheet" href="main.css"> | |
<script src="scrubber.js" charset="utf-8"></script> | |
<script src="//www.desmos.com/api/v0.8/calculator.js?apiKey=dcb31709b452b1cf9dc26972add0fda6"></script> | |
</head> | |
<body> | |
<div class="wrapper"> | |
<div class="slider-container"> | |
<div id="scrubber-left"> | |
<span id="p-label" class="scrubber-label">P_0=57</span> | |
</div> | |
<div id="scrubber-right"> | |
<span id="r-label" class="scrubber-label">r=0.46</span> | |
</div> | |
</div> | |
<div id="calculator"></div> | |
</div> | |
<script src="index.js" charset="utf-8"></script> | |
</body> | |
</html> |
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
(function() { | |
// Calculator state | |
var state = { | |
"version": 3, | |
"graph": { | |
"showGrid": false, | |
"showXAxis": true, | |
"showYAxis": true, | |
"xAxisStep": 1, | |
"yAxisStep": 100, | |
"xAxisMinorSubdivisions": 0, | |
"yAxisMinorSubdivisions": 0, | |
"xAxisArrowMode": "NONE", | |
"yAxisArrowMode": "NONE", | |
"xAxisLabel": "", | |
"yAxisLabel": "", | |
"xAxisNumbers": true, | |
"yAxisNumbers": true, | |
"polarMode": false, | |
"polarNumbers": true, | |
"degreeMode": false, | |
"projectorMode": false, | |
"squareAxes": false, | |
"viewport": { | |
"xmin": -1, | |
"ymin": -100, | |
"xmax": 10, | |
"ymax": 1000 | |
} | |
}, | |
"expressions": { | |
"list": [ | |
{ | |
"id": "5", | |
"type": "text", | |
"text": "Population function" | |
}, | |
{ | |
"id": "1", | |
"type": "expression", | |
"latex": "P\\left(t\\right)=P_0e^{rt}", | |
"domain": { | |
"min": "0", | |
"max": "1" | |
}, | |
"label": "", | |
"hidden": false, | |
"secret": false, | |
"color": "#000000", | |
"style": "normal", | |
"dragMode": "AUTO", | |
"residualVariable": "", | |
"regressionParameters": {}, | |
"isLogModeRegression": false | |
}, | |
{ | |
"id": "7", | |
"type": "folder", | |
"title": "Vector field", | |
"memberIds": { | |
"8": true, | |
"9": true, | |
"10": true, | |
"11": true | |
}, | |
"hidden": false, | |
"collapsed": true, | |
"secret": false | |
}, | |
{ | |
"id": "8", | |
"type": "expression", | |
"latex": "P_{prime}\\left(y\\right)=r\\cdot y", | |
"domain": { | |
"min": "0", | |
"max": "1" | |
}, | |
"label": "", | |
"hidden": true, | |
"secret": false, | |
"color": "#000000", | |
"style": "normal", | |
"dragMode": "AUTO", | |
"residualVariable": "", | |
"regressionParameters": {}, | |
"isLogModeRegression": false | |
}, | |
{ | |
"id": "9", | |
"type": "expression", | |
"latex": "i\\ =\\ \\left[0,\\ .5,\\ ...,\\ 10\\right]", | |
"domain": { | |
"min": "0", | |
"max": "1" | |
}, | |
"label": "", | |
"hidden": false, | |
"secret": false, | |
"color": "#c74440", | |
"style": "normal", | |
"dragMode": "AUTO", | |
"residualVariable": "", | |
"regressionParameters": {}, | |
"isLogModeRegression": false | |
}, | |
{ | |
"id": "11", | |
"type": "expression", | |
"latex": "j=\\left[50,\\ 100,\\ ...1000\\right]", | |
"domain": { | |
"min": "0", | |
"max": "1" | |
}, | |
"label": "", | |
"hidden": false, | |
"secret": false, | |
"color": "#388c46", | |
"style": "normal", | |
"dragMode": "AUTO", | |
"residualVariable": "", | |
"regressionParameters": {}, | |
"isLogModeRegression": false | |
}, | |
{ | |
"id": "10", | |
"type": "expression", | |
"latex": "y=P_{prime}\\left(j\\right)\\left(x-\\frac{\\operatorname{floor}\\left(2x\\right)}{2}\\right)\\left\\{x>0\\right\\}\\left\\{x-\\frac{\\operatorname{floor}\\left(2x\\right)}{2}<\\frac{.25}{\\sqrt{1+\\left(\\frac{P_{prime}\\left(j\\right)}{100}\\right)^2}}\\right\\}+j", | |
"domain": { | |
"min": "0", | |
"max": "1" | |
}, | |
"label": "", | |
"hidden": false, | |
"secret": false, | |
"color": "#fa7e19", | |
"style": "normal", | |
"dragMode": "AUTO", | |
"residualVariable": "", | |
"regressionParameters": {}, | |
"isLogModeRegression": false | |
} | |
] | |
} | |
}; | |
// Set up the calculator | |
var elt = document.getElementById('calculator'); | |
var options = { | |
expressions: false, | |
zoomButtons: false, | |
lockViewport: true, | |
settingsMenu: false, | |
branding: false | |
}; | |
var calc = Desmos.GraphingCalculator(elt, options); | |
calc.setState(state); | |
calc.setExpressions([ | |
{ id: 'p', latex: 'P_0=57' }, | |
{ id: 'r', latex: 'r=0.46', hidden: true } | |
]); | |
// Set up the labels | |
var pLabelElt = document.getElementById('p-label'); | |
var pLabel = Desmos.MathQuill.StaticMath(pLabelElt); | |
var rLabelElt = document.getElementById('r-label'); | |
var rLabel = Desmos.MathQuill.StaticMath(rLabelElt); | |
// Set up the scrubbers | |
var pElt = document.getElementById('scrubber-left'); | |
var pScrubber = new ScrubberView(); | |
pScrubber.min(1).max(200).value(57).step(1); | |
pScrubber.onValueChanged = function(val) { | |
var latex = 'P_0=' + val; | |
pLabel.latex(latex); | |
calc.setExpression({ id: 'p', latex: latex }); | |
}; | |
pElt.appendChild(pScrubber.elt); | |
var rElt = document.getElementById('scrubber-right'); | |
var rScrubber = new ScrubberView(); | |
rScrubber.min(0.01).max(3).value(0.46).step(0.01); | |
rScrubber.onValueChanged = function(val) { | |
var latex = 'r=' + val.toFixed(2); | |
rLabel.latex(latex); | |
calc.setExpression({ id: 'r', latex: latex }); | |
}; | |
rElt.appendChild(rScrubber.elt); | |
})(); |
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
.wrapper { | |
width: 500px; | |
height: 500px; | |
margin-left: auto; | |
margin-right: auto; | |
margin-top: 60px; | |
} | |
.slider-container { | |
display: flex; | |
-webkit-flex-direction: row; | |
flex-direction: row; | |
height: 90px; | |
border-left: 1px solid #ddd; | |
border-right: 1px solid #ddd; | |
border-top: 1px solid #ddd; | |
} | |
.scrubber { | |
width: 80%; | |
margin-left: auto; | |
margin-right: auto; | |
} | |
.scrubber-label { | |
margin-left: 10%; | |
} | |
#p-label { | |
margin-top: 15px; | |
margin-bottom: -10px; | |
} | |
#r-label { | |
margin-top: 15px; | |
margin-bottom: 1px; | |
} | |
#calculator { | |
padding-right: 2px; | |
height: 100%; | |
} | |
#scrubber-left, | |
#scrubber-right { | |
height: 100%; | |
width: 50% | |
} |
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
.scrubber { | |
margin-top: 10px; | |
width: 200px; | |
height: 40px; | |
position: relative; | |
} | |
.scrubber-vert { | |
margin-left: 10px; | |
width: 40px; | |
height: 200px; | |
position: relative; | |
} | |
.scrubber .track { | |
position: absolute; | |
top: 50%; | |
left: 0px; | |
width: 100%; | |
height: 6px; | |
background: #DDD; | |
border-radius: 3px; | |
margin-top: -3px; | |
} | |
.scrubber-vert .track { | |
position: absolute; | |
top: 0px; | |
height: 100%; | |
left: 50%; | |
width: 6px; | |
background: #DDD; | |
border-radius: 3px; | |
margin-left: -3px; | |
} | |
.scrubber .thumb { | |
-moz-box-sizing: border-box; | |
box-sizing: border-box; | |
position: absolute; | |
top: 50%; | |
left: 0px; | |
width: 22px; | |
height: 22px; | |
margin-left: -11px; | |
margin-top: -11px; | |
cursor: pointer; | |
opacity: 0.7; | |
border: 8px solid #BECFE4; | |
border-radius: 100%; | |
background: #4F81BD; | |
transition: border-width 0.2s ease 0s; | |
} | |
.scrubber-vert .thumb { | |
-moz-box-sizing: border-box; | |
box-sizing: border-box; | |
position: absolute; | |
top: 100%; | |
left: 50%; | |
width: 22px; | |
height: 22px; | |
margin-top: -11px; | |
margin-left: -11px; | |
cursor: pointer; | |
opacity: 0.7; | |
border: 8px solid #BECFE4; | |
border-radius: 100%; | |
background: #4F81BD; | |
transition: border-width 0.2s ease 0s; | |
} | |
.scrubber .thumb:hover, | |
.scrubber-vert .thumb:hover, | |
.thumb.dragging { | |
border-width: 0px; | |
opacity: 1; | |
} | |
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
function ScrubberView() { | |
this.makeAccessors(); | |
this.createDOM(); | |
this.attachListeners(); | |
this.onValueChanged = function () {}; | |
this.onScrubStart = function () {}; | |
this.onScrubEnd = function () {}; | |
} | |
ScrubberView.prototype.makeAccessors = function () { | |
var value = 0; | |
var min = 0; | |
var max = 1; | |
var step = 0; | |
var orientation = 'horizontal'; | |
this.value = function (_value) { | |
if (_value === undefined) return value; | |
if (value === _value) return this; | |
_value = Math.max(min, Math.min(max, _value)); | |
if (step > 0) { | |
var nsteps = Math.round((_value - min)/step); | |
var invStep = 1/step; | |
if (invStep === Math.round(invStep)) { | |
_value = (min*invStep + nsteps)/invStep; | |
} else { | |
_value = (min/step + nsteps)*step; | |
} | |
value = Math.max(min, Math.min(max, _value)); | |
} else { | |
value = _value; | |
} | |
this.redraw(); | |
this.onValueChanged(value); | |
return this; | |
}; | |
this.min = function (_min) { | |
if (_min === undefined) return min; | |
if (min === _min) return this; | |
min = _min; | |
this.redraw(); | |
return this; | |
}; | |
this.max = function (_max) { | |
if (_max === undefined) return max; | |
if (max === _max) return this; | |
max = _max; | |
this.redraw(); | |
return this; | |
}; | |
this.step = function (_step) { | |
if (_step === undefined) return step; | |
if (step === _step) return this; | |
step = _step; | |
this.redraw(); | |
return this; | |
}; | |
this.orientation = function(_orientation) { | |
if (_orientation === undefined) return orientation; | |
if (_orientation === orientation) return this; | |
orientation = _orientation; | |
this.redraw(); | |
return this; | |
}; | |
}; | |
ScrubberView.prototype.createDOM = function () { | |
this.elt = document.createElement('div'); | |
this.track = document.createElement('div'); | |
this.thumb = document.createElement('div'); | |
this.elt.className = this.orientation() === 'horizontal' ? 'scrubber' : 'scrubber-vert'; | |
this.track.className = 'track'; | |
this.thumb.className = 'thumb'; | |
this.elt.appendChild(this.track); | |
this.elt.appendChild(this.thumb); | |
}; | |
ScrubberView.prototype.redraw = function () { | |
var frac = (this.value() - this.min())/(this.max() - this.min()); | |
if (this.orientation() === 'horizontal') { | |
this.elt.className = 'scrubber'; | |
this.thumb.style.top = '50%'; | |
this.thumb.style.left = frac*100 + '%'; | |
} | |
else { | |
this.elt.className = 'scrubber-vert'; | |
this.thumb.style.left = '50%'; | |
this.thumb.style.top = 100 - (frac*100) + '%'; | |
} | |
}; | |
ScrubberView.prototype.attachListeners = function () { | |
var self = this; | |
var mousedown = false; | |
var cachedLeft; | |
var cachedWidth; | |
var cachedTop; | |
var cachedHeight; | |
var start = function (evt) { | |
evt.preventDefault(); | |
self.onScrubStart(self.value()); | |
mousedown = true; | |
var rect = self.elt.getBoundingClientRect(); | |
// NOTE: page[X|Y]Offset and the width and height | |
// properties of getBoundingClientRect are not | |
// supported in IE8 and below. | |
// | |
// Scrubber doesn't attempt to support IE<9. | |
var xOffset = window.pageXOffset; | |
var yOffset = window.pageYOffset; | |
cachedLeft = rect.left + xOffset; | |
cachedWidth = rect.width; | |
cachedTop = rect.top + yOffset; | |
cachedHeight = rect.height; | |
self.thumb.className += ' dragging'; | |
}; | |
var stop = function () { | |
mousedown = false; | |
cachedLeft = undefined; | |
cachedWidth = undefined; | |
cachedTop = undefined; | |
cachedHeight = undefined; | |
self.thumb.className = 'thumb'; | |
self.onScrubEnd(self.value()); | |
}; | |
var setValueFromPageX = function (pageX) { | |
var frac = Math.min(1, Math.max((pageX - cachedLeft)/cachedWidth, 0)); | |
self.value((1-frac)*self.min() + frac*self.max()); | |
}; | |
var setValueFromPageY = function (pageY) { | |
var frac = Math.min(1, Math.max(1 - (pageY - cachedTop)/cachedHeight, 0)); | |
self.value((1-frac)*self.min() + frac*self.max()); | |
}; | |
this.elt.addEventListener('mousedown', start); | |
this.elt.addEventListener('touchstart', start); | |
document.addEventListener('mousemove', function (evt) { | |
if (!mousedown) return; | |
evt.preventDefault(); | |
if (self.orientation() === 'horizontal') | |
setValueFromPageX(evt.pageX); | |
else | |
setValueFromPageY(evt.pageY); | |
}); | |
document.addEventListener('touchmove', function (evt) { | |
if (!mousedown) return; | |
evt.preventDefault(); | |
if (self.orientation() === 'horizontal') | |
setValueFromPageX(evt.changedTouches[0].pageX); | |
else | |
setValueFromPageY(evt.changedTouches[0].pageY); | |
}); | |
this.elt.addEventListener('mouseup', function (evt) { | |
if (!mousedown) return; | |
evt.preventDefault(); | |
if (self.orientation() === 'horizontal') | |
setValueFromPageX(evt.pageX); | |
else | |
setValueFromPageY(evt.pageY); | |
}); | |
this.elt.addEventListener('touchend', function (evt) { | |
if (!mousedown) return; | |
evt.preventDefault(); | |
if (self.orientation() === 'horizontal') | |
setValueFromPageX(evt.changedTouches[0].pageX); | |
else | |
setValueFromPageY(evt.changedTouches[0].pageY); | |
}); | |
document.addEventListener('mouseup', stop); | |
document.addEventListener('touchend', stop); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment