|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta charset="utf-8"> |
|
|
|
<script src="http://d3js.org/d3.v3.js"></script> |
|
|
|
<style> |
|
|
|
body { |
|
font: 13pt courier; |
|
font-weight: bold; |
|
background-color: #111; |
|
overflow-y:hidden; |
|
color: #ccc; |
|
-webkit-user-select: none; |
|
-moz-user-select: none; |
|
user-select: none; |
|
} |
|
|
|
.red_line { |
|
fill: none; |
|
stroke: crimson; |
|
stroke-width: 2px; |
|
stroke-linecap: round; |
|
opacity: 0.8; |
|
} |
|
|
|
.blue { |
|
fill: #66C; |
|
opacity:0.8; |
|
} |
|
|
|
.red { |
|
fill: crimson; |
|
opacity:0.8; |
|
} |
|
|
|
.blue_line { |
|
fill: none; |
|
stroke: #66C; |
|
stroke-width: 2px; |
|
stroke-linecap: round; |
|
opacity: 0.8; |
|
} |
|
|
|
.yellow { |
|
fill: #CC4 ; |
|
opacity:0.8; |
|
} |
|
|
|
</style> |
|
</head> |
|
<body> |
|
<script type="text/javascript"> |
|
var width = 320 ; |
|
var height = 320 ; |
|
var dom = 4 ; |
|
var maxL = dom * 2 / 5 ; |
|
var minL = 0 ; |
|
var maxL2 = maxL * maxL ; |
|
var minL2 = minL * minL ; |
|
var markHeight = 4 ; |
|
var pad = .45 ; |
|
|
|
var x = d3.scale.linear() |
|
.domain([-dom, dom]) |
|
.range([0, width]) ; |
|
|
|
var y = d3.scale.linear() |
|
.domain([-dom, dom]) |
|
.range([height, 0]) ; |
|
|
|
var invMark = x.invert(markHeight) - x.invert(0) ; |
|
var scale = 1 - invMark ; |
|
maxL -= invMark ; |
|
minL += invMark ; |
|
|
|
var line = d3.svg.line() |
|
.interpolate('basis') |
|
.x(function(d) { return x(d.x) ; }) |
|
.y(function(d) { return y(d.y) ; }) ; |
|
|
|
var svg = d3 |
|
.select("body") |
|
.append("svg") |
|
.attr("viewBox", '0 0 ' + 2 * width + ' ' + 2 * height) ; |
|
// .attr("preserveAspectRatio", "xMidYMin meet") ; |
|
// .style('max-height', '90%') |
|
// .style('min-height', '600px') ; |
|
|
|
svg |
|
.append('defs') |
|
.append('marker') |
|
.attr('id', 'redMarker') |
|
.attr('orient', 'auto') |
|
.attr('markerWidth', 2) |
|
.attr('markerHeight', markHeight) |
|
.attr('refX', 0.1) |
|
.attr('refY', 2) |
|
.append('path') |
|
.attr('d', 'M0,0 V4 L2,2 Z') |
|
.attr('fill', 'crimson') ; |
|
|
|
svg |
|
.append('marker') |
|
.attr('id', 'blueMarker') |
|
.attr('orient', 'auto') |
|
.attr('markerWidth', 2) |
|
.attr('markerHeight', markHeight) |
|
.attr('refX', 0.1) |
|
.attr('refY', 2) |
|
.append('path') |
|
.attr('d', 'M0,0 V4 L2,2 Z') |
|
.attr('fill', '#66C') ; |
|
|
|
var g = svg |
|
.append("g") |
|
.style('opacity', 0) ; |
|
//.attr("transform", "rotate(-60, 160, 160)") |
|
|
|
var gs = g.append('g') ; // more-or-less static stuff that is not part of the animation |
|
|
|
var unitCircle = gs |
|
.append('circle') |
|
.attr('fill', '#FFF') |
|
.attr('opacity', .05) |
|
.attr('r', x(1) - x(0)) |
|
.attr('cx', x(0)) |
|
.attr('cy', y(0)) |
|
|
|
var ga = g.append('g') ; |
|
|
|
var redLine = ga |
|
.append('path') |
|
.style('pointer-events', 'none') |
|
.attr('class', 'red_line') |
|
.attr('marker-end', 'url(#redMarker)') ; |
|
|
|
var gb = g.append('g') ; |
|
|
|
var blueLine = gb |
|
.append('path') |
|
.style('pointer-events', 'none') |
|
.attr('class', 'blue_line') |
|
.attr('marker-end', 'url(#blueMarker)') ; |
|
|
|
var root2 = Math.sqrt(2) ; |
|
var r2i = 1 / root2 ; |
|
var t0 = Math.PI / 2 - 0.1 - 0.5 * Math.random() ; |
|
var t1 = 0.2 + 0.3 * (Math.random() - 0.5) ; |
|
var l1 = 0.6 * maxL + (0.4 * Math.random() * maxL) ; |
|
var l2 = 0.6 * maxL + (0.4 * Math.random() * maxL) ; |
|
var da = [{x: 0, y: 0}, {x: Math.cos(t0) * scale * l1, y: Math.sin(t0) * scale * l1}] ; |
|
var db = [{x: 0, y: 0}, {x: Math.cos(t1) * scale * l2, y: Math.sin(t1) * scale * l2}] ; |
|
var dur = 710 ; |
|
var longDelay = 2 * dur ; |
|
|
|
redLine |
|
.datum(da) |
|
.attr("d", line) |
|
|
|
blueLine |
|
.datum(db) |
|
.attr("d", line) |
|
|
|
//var dragSwitch = true ; |
|
|
|
var length = function(d) { |
|
return Math.sqrt(d.x * d.x + d.y * d.y) ; |
|
} |
|
|
|
var circHover = function(node) { |
|
//dragSwitch = true ; |
|
d3.select(node) |
|
.transition() |
|
.ease('linear') |
|
.duration(dur * 0.5) |
|
.style('opacity', 0.3) ; |
|
} ; |
|
|
|
var circOut = function(node) { |
|
//dragSwitch = false ; |
|
d3.select(node) |
|
.transition() |
|
.ease('linear') |
|
.duration(dur * 0.5) |
|
.style('opacity', 0) ; |
|
} ; |
|
|
|
var unitSwitch = false ; |
|
|
|
var set_unit = function(animateSwitch) { |
|
if(animateSwitch === undefined) animateSwitch = false ; |
|
var xk, yk, l ; |
|
xk = da[1].x ; |
|
yk = da[1].y ; |
|
l = Math.sqrt(xk * xk + yk * yk) / scale ; |
|
da[1].x /= l ; |
|
da[1].y /= l ; |
|
xk = db[1].x ; |
|
yk = db[1].y ; |
|
l = Math.sqrt(xk * xk + yk * yk) / scale ; |
|
db[1].x /= l ; |
|
db[1].y /= l ; |
|
if(animateSwitch) { |
|
redLine |
|
.transition() |
|
.ease('linear') |
|
.duration(dur * 0.5) |
|
.attr('d', line) |
|
blueLine |
|
.transition() |
|
.ease('linear') |
|
.duration(dur * 0.5) |
|
.attr('d', line) ; |
|
redCircle |
|
.transition() |
|
.ease('linear') |
|
.duration(dur * 0.5) |
|
.attr('cx', x(da[1].x)) |
|
.attr('cy', y(da[1].y)) ; |
|
blueCircle |
|
.transition() |
|
.ease('linear') |
|
.duration(dur * 0.5) |
|
.attr('cx', x(db[1].x)) |
|
.attr('cy', y(db[1].y)) |
|
.each('end', vecText) ; |
|
aText |
|
.transition() |
|
.ease('linear') |
|
.duration(dur * 0.5) |
|
.attr('x', x(da[1].x + pad * da[1].x / length(da[1]))) |
|
.attr('y', y(da[1].y + pad * da[1].y / length(da[1]))) ; |
|
|
|
bText |
|
.transition() |
|
.ease('linear') |
|
.duration(dur * 0.5) |
|
.attr('x', x(db[1].x + pad * db[1].x / length(db[1]))) |
|
.attr('y', y(db[1].y + pad * db[1].y / length(db[1]))) ; |
|
} else { |
|
redLine.attr('d', line) ; |
|
blueLine.attr('d', line) ; |
|
redCircle |
|
.attr('cx', x(da[1].x)) |
|
.attr('cy', y(da[1].y)) ; |
|
blueCircle |
|
.attr('cx', x(db[1].x)) |
|
.attr('cy', y(db[1].y)) ; |
|
aText |
|
.attr('x', x(da[1].x + pad * da[1].x / length(da[1]))) |
|
.attr('y', y(da[1].y + pad * da[1].y / length(da[1]))) ; |
|
|
|
bText |
|
.attr('x', x(db[1].x + pad * db[1].x / length(db[1]))) |
|
.attr('y', y(db[1].y + pad * db[1].y / length(db[1]))) ; |
|
} |
|
} ; |
|
|
|
// |
|
// arc * angle: |
|
// |
|
|
|
var angle = function() { |
|
var angA = angl(da) ; |
|
var angB = angl(db) ; |
|
if(Math.abs(angA - angB) > Math.PI) { |
|
if(angA < 0 && angB > 0) { |
|
angA += 2 * Math.PI ; |
|
} else if(angA > 0 && angB < 0) { |
|
angB += 2 * Math.PI ; |
|
} else if(angA > 0 && angB > 0) { |
|
if(angA > Math.PI) { |
|
angA -= 2 * Math.PI ; |
|
} else { |
|
angB -= 2 * Math.PI ; |
|
} |
|
} else { |
|
if(angA < -Math.PI) { |
|
angA += 2 * Math.PI ; |
|
} else { |
|
angB += 2 * Math.PI ; |
|
} |
|
} |
|
} |
|
return [angA, angB] ; |
|
} ; |
|
|
|
var angl = function(d) { |
|
return (Math.PI / 2 - Math.atan2(d[1].y, d[1].x)) ; |
|
} ; |
|
|
|
var ang = angle() ; |
|
|
|
var arc = d3.svg.arc() |
|
.innerRadius(8) |
|
.outerRadius(10) |
|
.startAngle(ang[0]) |
|
.endAngle(ang[1]) ; |
|
|
|
var arcLine = gs |
|
.insert("path", ":first-child") |
|
.attr("d", arc) |
|
.attr('class', 'yellow') |
|
.attr("transform", 'translate(' + width / 2 + ',' + height / 2 + ')') ; |
|
|
|
var get_mid = function() { |
|
var lenA = Math.sqrt(da[1].x * da[1].x + da[1].y * da[1].y) ; |
|
var lenB = Math.sqrt(db[1].x * db[1].x + db[1].y * db[1].y) ; |
|
var xMid = 0.5 * (da[1].x / lenA + db[1].x / lenB) ; |
|
var yMid = 0.5 * (da[1].y / lenA + db[1].y / lenB) ; |
|
var mLen = Math.sqrt(xMid * xMid + yMid * yMid) ; |
|
var mscale = .5 / mLen ; |
|
xMid *= mscale ; |
|
yMid *= mscale ; |
|
return [xMid, yMid] ; |
|
} |
|
|
|
var mid = get_mid() ; |
|
|
|
var thetaText = gs |
|
.append('text') |
|
.attr('x', x(mid[0])) |
|
.attr('y', y(mid[1])) |
|
.text('θ') // |
|
.style('pointer-events', 'none') |
|
.style('font', 'courier') |
|
.style('font-size', '6pt') |
|
.attr('class', 'yellow') ; |
|
|
|
// |
|
// ****************** |
|
// ****** drag ****** |
|
// ****************** |
|
// |
|
var circDrag = function(d, element) { |
|
//var xy0 = d3.mouse(g.node()) ; |
|
//if(dragSwitch === false) { |
|
// return ; |
|
//} |
|
var dx = d3.event.dx ; |
|
var dy = d3.event.dy ; |
|
var dd = d.datum() ; |
|
var x1 = dd[1].x ; |
|
var y1 = dd[1].y ; |
|
var xi = x(x1) + dx ; |
|
var yi = y(y1) + dy ; |
|
x1 = x.invert(xi) ; |
|
y1 = y.invert(yi) ; |
|
var l2 = x1 * x1 + y1 * y1 ; |
|
var len = Math.sqrt(l2) ; |
|
if(l2 > maxL2) { |
|
var li = 1 / len ; |
|
x1 = x1 * li * maxL ; |
|
y1 = y1 * li * maxL ; |
|
len = maxL ; |
|
} |
|
if(l2 < minL2) { |
|
var li = 1 / len ; |
|
x1 = x1 * li * minL ; |
|
y1 = y1 * li * minL ; |
|
len = minL ; |
|
} |
|
|
|
d3.select(element) |
|
.attr('cx', x(x1)) |
|
.attr('cy', y(y1)) ; |
|
|
|
dd[1].x = x1 ; |
|
dd[1].y = y1 ; |
|
if(unitSwitch) { |
|
set_unit() ; |
|
} else { |
|
d.attr('d', line) ; // console.log('drag', element, xy0) ; |
|
} |
|
|
|
var ang = angle() ; |
|
|
|
arc |
|
.startAngle(ang[0]) |
|
.endAngle(ang[1]) ; |
|
|
|
arcLine.attr('d', arc) |
|
|
|
mid = get_mid() ; |
|
|
|
thetaText |
|
.attr('x', x(mid[0])) |
|
.attr('y', y(mid[1])) ; |
|
|
|
aText |
|
.attr('x', x(da[1].x + pad * da[1].x / length(da[1]))) |
|
.attr('y', y(da[1].y + pad * da[1].y / length(da[1]))) ; |
|
|
|
bText |
|
.attr('x', x(db[1].x + pad * db[1].x / length(db[1]))) |
|
.attr('y', y(db[1].y + pad * db[1].y / length(db[1]))) ; |
|
|
|
vecText() ; |
|
|
|
var dax = [{x: 0, y: 0}, {x: da[1].x, y: 0}] ; |
|
var day = [{x: 0, y: 0}, {x: 0, y: da[1].y}] ; |
|
|
|
var dbx = [{x: 0, y: 0}, {x: db[1].x, y: 0}] ; |
|
var dby = [{x: 0, y: 0}, {x: 0, y: db[1].y}] ; |
|
|
|
ax.datum(dax) ; |
|
ay.datum(day) ; |
|
|
|
bx.datum(dbx) ; |
|
by.datum(dby) ; |
|
|
|
ax.attr('d', line) ; |
|
ay.attr('d', line) ; |
|
|
|
bx.attr('d', line) ; |
|
by.attr('d', line) ; |
|
|
|
axText |
|
.attr('x', x(dax[1].x + 0.5 * pad * dax[1].x / length(dax[1])) + (shift * Math.min(0, dax[1].x / Math.abs(dax[1].x)))) |
|
.attr('y', y(dax[1].y + 0.5 * pad * dax[1].y / length(dax[1]))) ; |
|
|
|
ayText |
|
.attr('x', x(day[1].x + 0.5 * pad * day[1].x / length(day[1]))) |
|
.attr('y', y(day[1].y + 0.5 * pad * day[1].y / length(day[1])) - (shift * Math.min(0, day[1].y / Math.abs(day[1].y)))) ; |
|
|
|
bxText |
|
.attr('x', x(dbx[1].x + 0.5 * pad * dbx[1].x / length(dbx[1])) + (shift * Math.min(0, dbx[1].x / Math.abs(dbx[1].x)))) |
|
.attr('y', y(dbx[1].y + 0.5 * pad * dbx[1].y / length(dbx[1]))) ; |
|
|
|
byText |
|
.attr('x', x(dby[1].x + 0.5 * pad * dby[1].x / length(dby[1]))) |
|
.attr('y', y(dby[1].y + 0.5 * pad * dby[1].y / length(dby[1])) - (shift * Math.min(0, dby[1].y / Math.abs(dby[1].y)))) ; |
|
|
|
} ; |
|
|
|
// |
|
// ************************** |
|
// ****** primary text ****** |
|
// ************************** |
|
// |
|
|
|
var aText = ga |
|
.append('text') |
|
.text('a') |
|
.attr('x', x(da[1].x + pad * da[1].x / length(da[1]))) |
|
.attr('y', y(da[1].y + pad * da[1].y / length(da[1]))) |
|
.style('pointer-events', 'none') |
|
.style('font', 'courier') |
|
.style('font-size', '12pt') |
|
.attr('class', 'red') ; |
|
|
|
var bText = gb |
|
.append('text') |
|
.text('b') |
|
.attr('x', x(db[1].x + pad * db[1].x / length(db[1]))) |
|
.attr('y', y(db[1].y + pad * db[1].y / length(db[1]))) |
|
.style('pointer-events', 'none') |
|
.style('font', 'courier') |
|
.style('font-size', '12pt') |
|
.attr('class', 'blue') ; |
|
|
|
var redCircle = ga |
|
.insert('circle', ":first-child") |
|
.datum(redLine) |
|
.attr('fill', '#0C0') |
|
.attr('opacity', 0) |
|
.attr('r', 20) |
|
.attr('cx', x(da[1].x)) |
|
.attr('cy', y(da[1].y)) |
|
.on('mouseup', function() { circOut(this) }) |
|
.on('mousedown', function() { circHover(this) }) |
|
.on('mouseleave', function() { circOut(this) }) |
|
.on('mouseenter', function() { circHover(this) }) ; |
|
|
|
var blueCircle = gb |
|
.insert('circle', ":first-child") |
|
.datum(blueLine) |
|
.attr('fill', '#0C0') |
|
.attr('opacity', 0) |
|
.attr('r', 20) |
|
.attr('cx', x(db[1].x)) |
|
.attr('cy', y(db[1].y)) |
|
.on('mouseup', function() { circOut(this) }) |
|
.on('mousedown', function() { circHover(this) }) |
|
.on('mouseleave', function() { circOut(this) }) |
|
.on('mouseenter', function() { circHover(this) }) ; |
|
|
|
blueCircle |
|
.call(d3.behavior.drag() |
|
.origin(Object) |
|
.on("dragstart", function(d) { |
|
circHover(blueCircle.node()) ; |
|
blueCircle |
|
.on('mouseup', null) |
|
.on('mousedown', null) |
|
.on('mouseleave', null) |
|
.on('mouseenter', null) ; |
|
}) |
|
.on("drag", function(d) { circDrag(d, this) } ) |
|
.on("dragend", function(d) { |
|
circOut(blueCircle.node()) |
|
blueCircle |
|
.on('mouseup', function() { circOut(this) }) |
|
.on('mousedown', function() { circHover(this) }) |
|
.on('mouseleave', function() { circOut(this) }) |
|
.on('mouseenter', function() { circHover(this) }) ; |
|
}) |
|
) ; |
|
|
|
redCircle |
|
.call(d3.behavior.drag() |
|
.origin(Object) |
|
.on("dragstart", function(d) { |
|
circHover(redCircle.node()) ; |
|
redCircle |
|
.on('mouseup', null) |
|
.on('mousedown', null) |
|
.on('mouseleave', null) |
|
.on('mouseenter', null) ; |
|
}) |
|
.on("drag", function(d) { circDrag(d, this) } ) |
|
.on("dragend", function(d) { |
|
circOut(redCircle.node()) |
|
redCircle |
|
.on('mouseup', function() { circOut(this) }) |
|
.on('mousedown', function() { circHover(this) }) |
|
.on('mouseleave', function() { circOut(this) }) |
|
.on('mouseenter', function() { circHover(this) }) ; |
|
}) |
|
) ; |
|
|
|
var unitBox = gs |
|
.append('rect') ; |
|
|
|
var unitToggle = function() { |
|
var fill ; |
|
if(unitSwitch) { |
|
unitSwitch = false ; |
|
fill = '#111' ; |
|
} else { |
|
unitSwitch = true ; |
|
fill = '#999' ; |
|
var animateSwitch = true ; |
|
set_unit(animateSwitch) ; |
|
} |
|
unitBox |
|
.transition() |
|
.duration(100) |
|
.ease('linear') |
|
.attr('fill', fill) ; |
|
} ; |
|
|
|
unitBox |
|
.attr('x', 20) |
|
.attr('y', 10) |
|
.attr('rx', 2) |
|
.attr('ry', 2) |
|
.attr('fill', '#111') |
|
.attr('width', 10) |
|
.attr('height', 10) |
|
.attr('stroke', '#CCC') |
|
.attr('stroke-width', 1.5) |
|
.style('cursor', 'pointer') |
|
.on('click', unitToggle) ; |
|
|
|
var unitText = gs |
|
.append('text') ; |
|
|
|
unitText |
|
.attr('x', 34) |
|
.attr('y', 17) |
|
.text('unit length') |
|
.style('font', 'courier') |
|
.style('font-size', '6pt') |
|
.style('fill', '#ccc') |
|
.style('cursor', 'pointer') |
|
.on('click', unitToggle) ; |
|
|
|
var dotDef = gs |
|
.append('text') |
|
.attr('x', 120) |
|
.attr('y', 42) |
|
.style('font', 'courier') |
|
.style('font-size', '6pt') |
|
.style('fill', '#ccc') ; |
|
|
|
var boxy = d3.scale.linear() |
|
.domain([-maxL * maxL, maxL * maxL]) |
|
.range([-200 / height, 200 / height]) ; |
|
|
|
var boxc = d3.scale.linear() |
|
.domain([-maxL * maxL, 0, maxL * maxL]) |
|
.range([d3.rgb('#95F'), d3.rgb('#959'), d3.rgb('#F59')]) ; |
|
|
|
var dotbarX = 103 ; |
|
|
|
var dotBar = gs.append('path') |
|
.datum([{x: x.invert(dotbarX), y: y.invert(42)}, {x: x.invert(dotbarX), y: y.invert(42) + boxy(0)}]) |
|
.attr('d', line) |
|
.attr('stroke-width', 10) |
|
.attr('stroke', boxc(1)) ; |
|
|
|
var unscale = function(d) { |
|
var len = length(d) ; |
|
len = (len + invMark) / len ; |
|
return [d.x * len, d.y * len] ; |
|
} ; |
|
|
|
var aVec = gs |
|
.append('text') |
|
.attr('x', 120) |
|
.attr('y', 10) |
|
.style('font', 'courier') |
|
.style('font-size', '6pt') |
|
.style('fill', '#ccc') ; |
|
|
|
var aLen = gs |
|
.append('text') |
|
.attr('x', 240) |
|
.attr('y', 10) |
|
.style('font', 'courier') |
|
.style('font-size', '6pt') |
|
.style('fill', '#ccc') ; |
|
|
|
var bVec = gs |
|
.append('text') |
|
.attr('x', 120) |
|
.attr('y', 20) |
|
.style('font', 'courier') |
|
.style('font-size', '6pt') |
|
.style('fill', '#ccc') ; |
|
|
|
var bLen = gs |
|
.append('text') |
|
.attr('x', 240) |
|
.attr('y', 20) |
|
.style('font', 'courier') |
|
.style('font-size', '6pt') |
|
.style('fill', '#ccc') ; |
|
|
|
var thetaVal = gs |
|
.append('text') |
|
.attr('x', 120) |
|
.attr('y', 30) |
|
.style('font', 'courier') |
|
.style('font-size', '6pt') |
|
.style('fill', '#ccc') ; |
|
|
|
var cosVal = gs |
|
.append('text') |
|
.attr('x', 240) |
|
.attr('y', 30) |
|
.style('font', 'courier') |
|
.style('font-size', '6pt') |
|
.style('fill', '#ccc') ; |
|
|
|
var vecText = function() { |
|
var a = unscale(da[1]) ; |
|
var b = unscale(db[1]) ; |
|
var dot = a[0] * b[0] + a[1] * b[1] ; |
|
aVec.html('a = (' + a[0].toPrecision(3) + ', ' + a[1].toPrecision(3) + ')') ; |
|
bVec.html('b = (' + b[0].toPrecision(3) + ', ' + b[1].toPrecision(3) + ')') ; |
|
aLen.html('|a| = ' + Math.sqrt(a[0] * a[0] + a[1] * a[1]).toPrecision(4)) ; |
|
bLen.html('|b| = ' + Math.sqrt(b[0] * b[0] + b[1] * b[1]).toPrecision(4)) ; |
|
var ang = angle() ; |
|
thetaVal.html('θ = ' + Math.abs(ang[0] - ang[1]).toPrecision(3)) ; |
|
cosVal.html('cos θ = ' + Math.cos(Math.abs(ang[0] - ang[1])).toPrecision(3)) ; |
|
dotDef.html('⟨a, b⟩ = |a| |b| cos θ = a1 b1 + a2 b2 = ' + dot.toPrecision(3)) ; |
|
dotBar |
|
.datum([{x: x.invert(dotbarX), y: y.invert(42)}, {x: x.invert(dotbarX), y: y.invert(42) + boxy(dot)}]) |
|
.attr('stroke', boxc(dot)) |
|
.attr('d', line) |
|
|
|
} ; |
|
|
|
vecText() ; |
|
|
|
// |
|
// **************************** |
|
// ****** secondary text ****** |
|
// **************************** |
|
// |
|
|
|
var atx = Number(aText.attr('x')) ; // + (txtShift * Number(aText.attr('x')) / Math.abs(Number(aText.attr('x')))) ; |
|
var aty = Number(aText.attr('y')) ; // + (txtShift * Number(aText.attr('y')) / Math.abs(Number(aText.attr('y')))) ; |
|
var btx = Number(bText.attr('x')) ; // + (txtShift * Number(bText.attr('x')) / Math.abs(Number(bText.attr('x')))) ; |
|
var bty = Number(bText.attr('y')) ; // + (txtShift * Number(bText.attr('y')) / Math.abs(Number(bText.attr('y')))) ; |
|
|
|
var yCut = 20 ; |
|
var dya = Math.abs(x(da[1].y) - x(0)) ; |
|
var dyb = Math.abs(x(db[1].y) - x(0)) ; |
|
|
|
if(dya < yCut) { |
|
aty += yCut * dya / Math.abs(dya) ; |
|
} |
|
if(dyb < yCut) { |
|
bty += yCut * dyb / Math.abs(dyb) ; |
|
} |
|
|
|
ga1 = ga |
|
.append('g') |
|
.style('opacity', 0) ; |
|
|
|
gb1 = gb |
|
.append('g') |
|
.style('opacity', 0) ; |
|
|
|
gax = ga1.append('g') ; |
|
gay = ga1.append('g') ; |
|
gbx = gb1.append('g') ; |
|
gby = gb1.append('g') ; |
|
|
|
var ax = gax |
|
.append('path') |
|
.attr('class', 'red_line') |
|
.attr('marker-end', 'url(#redMarker)') ; |
|
|
|
var ay = gay |
|
.append('path') |
|
.attr('class', 'red_line') |
|
.attr('marker-end', 'url(#redMarker)') ; |
|
|
|
var bx = gbx |
|
.append('path') |
|
.attr('class', 'blue_line') |
|
.attr('marker-end', 'url(#blueMarker)') ; |
|
|
|
var by = gby |
|
.append('path') |
|
.attr('class', 'blue_line') |
|
.attr('marker-end', 'url(#blueMarker)') ; |
|
|
|
var dax = [{x: 0, y: 0}, {x: da[1].x, y: 0}] ; |
|
var day = [{x: 0, y: 0}, {x: 0, y: da[1].y}] ; |
|
|
|
var dbx = [{x: 0, y: 0}, {x: db[1].x, y: 0}] ; |
|
var dby = [{x: 0, y: 0}, {x: 0, y: db[1].y}] ; |
|
|
|
ax.datum(dax) ; |
|
ay.datum(day) ; |
|
|
|
bx.datum(dbx) ; |
|
by.datum(dby) ; |
|
|
|
ax.attr('d', line) ; |
|
ay.attr('d', line) ; |
|
|
|
bx.attr('d', line) ; |
|
by.attr('d', line) ; |
|
|
|
var shift = 15 ; |
|
|
|
var axText = gax |
|
.append('text') |
|
.html('ax') |
|
.attr('x', x(dax[1].x + 0.5 * pad * dax[1].x / length(dax[1])) + (shift * Math.min(0, dax[1].x / Math.abs(dax[1].x)))) |
|
.attr('y', y(dax[1].y + 0.5 * pad * dax[1].y / length(dax[1]))) |
|
.style('pointer-events', 'none') |
|
.style('font', 'courier') |
|
.style('font-size', '10pt') |
|
.attr('class', 'red') |
|
.style('opacity', 0) ; |
|
|
|
var ayText = gay |
|
.append('text') |
|
.html('ay') |
|
.attr('x', x(day[1].x + 0.5 * pad * day[1].x / length(day[1]))) |
|
.attr('y', y(day[1].y + 0.5 * pad * day[1].y / length(day[1])) - (shift * Math.min(0, day[1].y / Math.abs(day[1].y)))) |
|
.style('pointer-events', 'none') |
|
.style('font', 'courier') |
|
.style('font-size', '10pt') |
|
.attr('class', 'red') |
|
.style('opacity', 0) ; |
|
|
|
var bxText = gbx |
|
.append('text') |
|
.html('bx') |
|
.attr('x', x(dbx[1].x + 0.5 * pad * dbx[1].x / length(dbx[1])) + (shift * Math.min(0, dbx[1].x / Math.abs(dbx[1].x)))) |
|
.attr('y', y(dbx[1].y + 0.5 * pad * dbx[1].y / length(dbx[1]))) |
|
.style('pointer-events', 'none') |
|
.style('font', 'courier') |
|
.style('font-size', '10pt') |
|
.attr('class', 'blue') |
|
.style('opacity', 0) ; |
|
|
|
var byText = gby |
|
.append('text') |
|
.html('by') |
|
.attr('x', x(dby[1].x + 0.5 * pad * dby[1].x / length(dby[1]))) |
|
.attr('y', y(dby[1].y + 0.5 * pad * dby[1].y / length(dby[1])) - (shift * Math.min(0, dby[1].y / Math.abs(dby[1].y)))) |
|
.style('pointer-events', 'none') |
|
.style('font', 'courier') |
|
.style('font-size', '10pt') |
|
.attr('class', 'blue') |
|
.style('opacity', 0) ; |
|
|
|
g |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
// |
|
// *********************** |
|
// ****** animation ****** |
|
// *********************** |
|
// |
|
var animating = false ; |
|
|
|
var yShift = 60 ; |
|
var xShifta = -width * 0.15 ; |
|
var xShiftb = width * 0.65 ; |
|
|
|
var ga1 ; |
|
var gb1 ; |
|
|
|
var gax ; |
|
var gay ; |
|
var gbx ; |
|
var gby ; |
|
|
|
var gdt ; |
|
|
|
var gc = g |
|
.append('g') |
|
.style('opacity', 0) ; |
|
|
|
var gn = gc |
|
.append('g') |
|
.style('pointer-events', 'all') |
|
.style('opacity', 0) ; |
|
|
|
var animate = function() { |
|
if(animating) return ; // don't respond to animation requests until previous one has completed (one at a time) |
|
animating = true ; |
|
|
|
gs |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 0) |
|
.each('end', function() { |
|
goText |
|
.transition() |
|
.duration(dur) |
|
.style('opacity', 0) |
|
.transition() |
|
.delay(dur) |
|
.duration(dur) |
|
.text('back') |
|
.style('opacity', 1) ; |
|
|
|
goBox |
|
.on('click', null) |
|
.on('click', home) ; |
|
|
|
}) ; |
|
|
|
//ga.style('opacity', 0) ; |
|
//gb.style('opacity', 0) ; |
|
|
|
ga1 |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
|
|
gb1 |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
|
|
gc |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
|
|
aText |
|
.html('a = ax + ay') |
|
.transition() |
|
.duration(longDelay) |
|
.ease('linear') |
|
.style('opacity', 0.4) |
|
.style('font-size', '6pt') ; |
|
|
|
bText |
|
.html('b = bx + by') |
|
.transition() |
|
.duration(longDelay) |
|
.ease('linear') |
|
.style('opacity', 0.4) |
|
.style('font-size', '6pt') ; |
|
|
|
byText |
|
.transition() |
|
.duration(dur) |
|
.delay(longDelay) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
|
|
bxText |
|
.transition() |
|
.duration(dur) |
|
.delay(longDelay) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
|
|
ayText |
|
.transition() |
|
.duration(dur) |
|
.delay(longDelay) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
|
|
axText |
|
.transition() |
|
.duration(dur) |
|
.delay(longDelay) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
|
|
redLine |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 0.4) ; |
|
|
|
blueLine |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 0.4) ; |
|
|
|
// var txtShift = -40 ; |
|
|
|
var gScale = 1 ; |
|
|
|
g |
|
.transition() |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.attr('transform', 'translate(' + width/4 + ',' + height/4 + ')scale(' + gScale + ')translate(' + -width/4 + ',' + -height/4 + ')') ; |
|
|
|
ga |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 1) |
|
.attr('transform', 'translate(' + xShifta + ',' + yShift + ')translate(' + width/4 + ',' + height/4 + ')scale(1)translate(' + -width/4 + ',' + -height/4 + ')') ; |
|
|
|
gb |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 1) |
|
.attr('transform', 'translate(' + xShiftb + ',' + yShift + ')translate(' + width/4 + ',' + height/4 + ')scale(1)translate(' + -width/4 + ',' + -height/4 + ')') ; |
|
|
|
var xgc = 60 ; |
|
|
|
var dTxt = gc |
|
.append('text') |
|
.attr('x', xgc + 20) |
|
.attr('y', 35) |
|
.html('⟨a, b⟩ =') |
|
.style('fill', '#999') |
|
.style('pointer-events', 'none') |
|
.style('font', 'courier') |
|
.style('font-size', '10pt') ; |
|
|
|
var dotTxt = gc |
|
.append('text') |
|
.attr('x', xgc + 90) |
|
.attr('y', 35) |
|
.html('⟨ax + ay, bx + by⟩ ') |
|
.style('fill', '#999') |
|
.style('pointer-events', 'none') |
|
.style('font', 'courier') |
|
.style('font-size', '10pt') ; |
|
|
|
gc |
|
.transition() |
|
.delay(dur) |
|
.duration(2 * dur) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
|
|
gdt = gc |
|
.append('g') |
|
.style('opacity', 0) ; |
|
|
|
var anim2 = function() { |
|
|
|
nextButton.on('click', null) ; |
|
fade_out(gn) ; |
|
|
|
dotTxt |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 0) |
|
.remove() |
|
.each('end', function() { |
|
dotTxt = [] ; |
|
dTxt = [['x', 'x'], ['x', 'y'], ['y', 'x'], ['y', 'y']] ; |
|
tTxt = ['0', 'π/2', 'π/2', '0'] ; |
|
|
|
for(var k = 0 ; k < 4 ; k++) { |
|
dotTxt[k] = gdt |
|
.append('g') |
|
.style('pointer-events', 'all') ; |
|
|
|
dotTxt[k] |
|
.append('text') |
|
.attr('x', xgc + 90 + k * 85) |
|
.attr('y', 35) |
|
.style('fill', '#999') |
|
.style('pointer-events', 'none') |
|
.style('font', 'courier') |
|
.style('font-size', '10pt') ; |
|
|
|
dotTxt[k] |
|
.append('rect') |
|
.attr('x', xgc + 90 + k * 85) |
|
.attr('y', 20) |
|
.attr('width', 60) |
|
.attr('height', 25) |
|
.attr('fill', '#FFF') |
|
.attr('opacity', '.01') ; |
|
|
|
var sk = '⟨a' + dTxt[k][0] + ', b' + dTxt[k][1] + '⟩' ; |
|
if(k < 3) { |
|
gdt |
|
.append('text') |
|
.attr('x', xgc + 159 + k * 85) |
|
.attr('y', 35) |
|
.style('fill', '#999') |
|
.style('pointer-events', 'none') |
|
.style('font', 'courier') |
|
.style('font-size', '10pt') |
|
.text('+') ; |
|
} |
|
dotTxt[k].select('text').html(sk) ; |
|
} |
|
|
|
dotTxt[0] |
|
.on('mouseenter', function() { |
|
dotTxt[0] |
|
.select('text') |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('fill', '#FF9') ; |
|
|
|
arrows_up(gax, gbx) ; |
|
var k = 0 ; |
|
animTxt |
|
.html('|a' + dTxt[k][0] + '| |b' + dTxt[k][1] + '| cos ' + tTxt[k] + ' = a1 b1') |
|
.transition() |
|
.duration(0.5 * dur) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
}) |
|
.on('mouseleave', function() { |
|
dotTxt[0] |
|
.select('text') |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('fill', '#999') ; |
|
arrows_down(gax, gbx) ; |
|
fade_out(animTxt) ; |
|
}) |
|
|
|
dotTxt[1] |
|
.on('mouseenter', function() { |
|
dotTxt[1] |
|
.select('text') |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('fill', '#FF9') ; |
|
arrows_up(gax, gby) |
|
var k = 1 ; |
|
animTxt.html('|a' + dTxt[k][0] + '| |b' + dTxt[k][1] + '| cos ' + tTxt[k] + ' = 0') |
|
.transition() |
|
.duration(0.5 * dur) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
}) |
|
.on('mouseleave', function() { |
|
dotTxt[1] |
|
.select('text') |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('fill', '#999') ; |
|
arrows_down(gax, gby) |
|
fade_out(animTxt) ; |
|
}) |
|
|
|
dotTxt[2] |
|
.on('mouseenter', function() { |
|
dotTxt[2] |
|
.select('text') |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('fill', '#FF9') ; |
|
arrows_up(gay, gbx) |
|
var k = 2 ; |
|
animTxt.html('|a' + dTxt[k][0] + '| |b' + dTxt[k][1] + '| cos ' + tTxt[k] + ' = 0') |
|
.transition() |
|
.duration(0.5 * dur) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
}) |
|
.on('mouseleave', function() { |
|
dotTxt[2] |
|
.select('text') |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('fill', '#999') ; |
|
arrows_down(gay, gbx) |
|
fade_out(animTxt) ; |
|
}) |
|
|
|
dotTxt[3] |
|
.on('mouseenter', function() { |
|
dotTxt[3] |
|
.select('text') |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('fill', '#FF9') ; |
|
arrows_up(gay, gby) |
|
var k = 3 ; |
|
animTxt |
|
.html('|a' + dTxt[k][0] + '| |b' + dTxt[k][1] + '| cos ' + tTxt[k] + ' = a2 b2') |
|
.transition() |
|
.duration(0.5 * dur) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
}) |
|
.on('mouseleave', function() { |
|
dotTxt[3] |
|
.select('text') |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('fill', '#999') ; |
|
arrows_down(gay, gby) |
|
fade_out(animTxt) ; |
|
}) |
|
|
|
var animTxt = gdt |
|
.append('text') |
|
.attr('x', 160) |
|
.attr('y', 80) |
|
.style('opacity', 1) |
|
.style('fill', '#FF9') |
|
.style('pointer-events', 'none') |
|
.style('font', 'courier') |
|
.style('font-size', '10pt') ; |
|
|
|
var pause = 4 * dur ; |
|
|
|
animTxt.text('mouse over the terms above to animate') |
|
|
|
gdt |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
}) ; |
|
} ; |
|
|
|
var nextButton = gn |
|
.append('rect') |
|
.attr('x', 10) |
|
.attr('y', 46) |
|
.attr('rx', 2) |
|
.attr('ry', 2) |
|
.attr('width', 46) |
|
.attr('height', 15) |
|
.attr('fill', '#CCC') |
|
.attr('stroke', 'none') |
|
.style('cursor', 'pointer') |
|
.on('click', anim2) ; |
|
|
|
var nextTxt = gn |
|
.append('text') |
|
.attr('x', 18.5) |
|
.attr('y', 56.5) |
|
.text('next') |
|
.style('font', 'courier') |
|
.style('font-size', '7pt') |
|
.style('fill', '#111') |
|
.style('pointer-events', 'none') ; |
|
|
|
gn |
|
.transition() |
|
.delay(longDelay) |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
|
|
} ; |
|
|
|
var fade_out = function(element) { |
|
element |
|
.transition() |
|
.duration(0.5 * dur) |
|
.ease('linear') |
|
.style('opacity', 0) ; |
|
} |
|
|
|
var home = function() { |
|
redLine |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
|
|
blueLine |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 1) ; |
|
|
|
aText |
|
.html('a') |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 1) |
|
.style('font-size', '12pt') |
|
|
|
bText |
|
.html('b') |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 1) |
|
.style('font-size', '12pt') |
|
|
|
ga1 |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 0) ; |
|
|
|
gb1 |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 0) ; |
|
|
|
gc |
|
.transition() |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 0) |
|
.each('end', function() { |
|
gdt.remove() ; |
|
}) ; |
|
|
|
ga |
|
.transition() |
|
.delay(dur) |
|
.duration(dur) |
|
.ease('linear') |
|
.attr('transform', 'translate(0,0)scale(1)translate(0,0)') ; |
|
|
|
gb |
|
.transition() |
|
.delay(dur) |
|
.duration(dur) |
|
.ease('linear') |
|
.attr('transform', 'translate(0,0)scale(1)translate(0,0)') ; |
|
|
|
g |
|
.transition() |
|
.delay(dur) |
|
.duration(dur) |
|
.ease('linear') |
|
.attr('transform', 'translate(0,0)scale(1)translate(0,0)') ; |
|
|
|
gs |
|
.transition() |
|
.delay(2 * dur) |
|
.duration(dur) |
|
.ease('linear') |
|
.style('opacity', 1) |
|
.each('end', function() { |
|
axText.style('opacity', 0) ; |
|
bxText.style('opacity', 0) ; |
|
ayText.style('opacity', 0) ; |
|
byText.style('opacity', 0) ; |
|
gn.style('opacity', 0) ; |
|
goText |
|
.transition() |
|
.duration(dur) |
|
.style('opacity', 0) |
|
.transition() |
|
.delay(dur) |
|
.duration(0.5 * dur) |
|
.text('start') |
|
.style('opacity', 1) ; |
|
|
|
goBox |
|
.on('click', null) |
|
.on('click', animate) ; |
|
}) ; |
|
|
|
animating = false ; |
|
} |
|
|
|
var yShift2 = -0.8 * yShift ; |
|
var xShift = 0.5 * (Math.abs(xShifta) + Math.abs(xShiftb)) ; |
|
var xShift1 = xShift ; |
|
var xShift2 = -xShift ; |
|
|
|
var arrows_up = function(a, b) { |
|
a |
|
.transition() |
|
.delay(.2 * dur) |
|
.duration(dur) |
|
.ease('linear') |
|
.attr('transform', 'translate(' + xShift1 + ',' + yShift2 + ')') ; |
|
b |
|
.transition() |
|
.delay(.2 * dur) |
|
.duration(dur) |
|
.ease('linear') |
|
.attr('transform', 'translate(' + xShift2 + ',' + yShift2 + ')') ; |
|
} ; |
|
|
|
var arrows_down = function(a, b) { |
|
a |
|
.transition() |
|
.delay(.2 * dur) |
|
.duration(dur) |
|
.ease('linear') |
|
.attr('transform', 'translate(0,0)') ; |
|
b |
|
.transition() |
|
.delay(.2 * dur) |
|
.duration(dur) |
|
.ease('linear') |
|
.attr('transform', 'translate(0,0)') ; |
|
} ; |
|
|
|
var gbox = g.append('g') ; |
|
|
|
var goBox = gbox.append('rect') |
|
.attr('x', 10) |
|
.attr('y', 29) |
|
.attr('rx', 2) |
|
.attr('ry', 2) |
|
.attr('fill', '#CCC') |
|
.attr('width', 46) |
|
.attr('height', 15) |
|
.attr('stroke', 'none') |
|
.style('cursor', 'pointer') |
|
.on('click', animate) ; |
|
|
|
var goText = gbox |
|
.append('text') ; |
|
|
|
goText |
|
.attr('x', 19) |
|
.attr('y', 39.5) |
|
.text('start') |
|
.style('font', 'courier') |
|
.style('font-size', '7pt') |
|
.style('fill', '#111') |
|
.style('pointer-events', 'none') ; |
|
|
|
</script> |
|
</body> |
|
</html> |