summaryrefslogtreecommitdiff
path: root/index.html
blob: 31a4240d66fd06f3e91b362aa97e210e7c9a3c64 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Wadsworth/Wheatstone</title>
<style>
svg {
	display: block;
	margin: 1em auto;
	border: 1px solid black;
}
.pointer, .plate {
	transform: none;
	transform-origin: 50% 50%;
}
.pointer .plate path {
	stroke: none;
	stroke-width: none;
}
.pointer path, .pointer circle {
	stroke: black;
	stroke-width: 1px;
}
.pointer circle {
	fill: transparent;
}
</style>
<script>
let dragging = null;
let svg = null;
function start(element) { dragging = element; }
function stop() { dragging = null; }
function update(element, diff) {
	element.dataset.angle = Number(element.dataset.angle) + diff;
	element.style.transform = `rotate(${element.dataset.angle}deg)`;
}
function reset() {
	if (svg) {
		svg.querySelectorAll(".pointer").forEach((x) => {
			x.dataset.angle = 0;
			update(x, 0);
		});
	}
}
function onmove(event) {
	if (dragging) {
		const rect = svg.getBoundingClientRect();
		const x = event.clientX - (rect.left + rect.width / 2);
		const y = event.clientY - (rect.top + rect.height / 2);
		const theta = Math.atan2(y, x) - Math.atan2(y - event.movementY, x - event.movementX);
		let diff = theta * 180.0 / Math.PI;
		if (diff > 180) { diff -= 360.0; }  // We jump back full circle to keep the diff values meaningful in context.
		if (diff < -180) { diff += 360.0; }
		update(dragging, diff);
		svg.querySelectorAll(".pointer").forEach((x) => {
			if (x != dragging) {
				const ratio = Number(dragging.dataset.ratio) / Number(x.dataset.ratio);
				update(x, diff * ratio);
			}
		});
	}
}
function change(query, ratio) {
	svg.querySelector(query).dataset.ratio = ratio;
}
function newSegment(radius, n) {
	let g = document.createElementNS("http://www.w3.org/2000/svg", "g");
	let path = document.createElementNS("http://www.w3.org/2000/svg", "path");
	const outerRadius = radius + 10;
	const left = width(radius);
	const right = width(outerRadius);
	path.setAttribute("d", `M${left} ${-height(radius)}
		A${radius} ${radius} 0 0 1 ${left} ${height(radius)}
		L${right} ${height(outerRadius)}
		A${outerRadius} ${outerRadius} 0 0 0 ${right} ${-height(outerRadius)}
		Z`);
	g.classList.add("plate");
	g.appendChild(path);
	return g;

	function width(radius) { return Math.cos(Math.PI / n) * radius; }
	function height(radius) { return Math.sin(Math.PI / n) * radius; }
}
function generatePlate(svg, radius, segments) {
	const colours = ["#edd", "#ded", "#dde"];
	for (let i=0; i < segments; i++) {
		let segment = newSegment(radius, segments);
		segment.style.transform = `rotate(${i * 360 / segments}deg) translate(50%, 50%)`;
		segment.style.fill = colours[i%3];
		svg.prepend(segment);
	}
}
function init(element) {
	svg = element;
	generatePlate(svg, 20, 26);
	generatePlate(svg, 30, 27);
}
</script>
<svg onload="init(this)" onmousemove="onmove(event)" onmouseleave="stop()" onmouseup="stop()" viewBox="0 0 100 100">
<g id="outer" data-angle="0" data-ratio="27" class="pointer">
	<path d="M49 50 L80 50 Z"/>
	<circle onmousedown="start(this.parentNode)" cx="85" cy="50" r="5"/>
</g>
<g id="inner" data-angle="0" data-ratio="26" class="pointer">
	<path d="M49 50 L70 50 Z"/>
	<circle onmousedown="start(this.parentNode)" cx="75" cy="50" r="5"/>
</g>
</svg>
<strong>Wadsworth/Wheatstone</strong><br>
Outer: <input onchange='change("#outer", this.value)' type="number" value="27" autocomplete="off"><br>
Inner: <input onchange='change("#inner", this.value)' type="number" value="26" autocomplete="off"><br>
<button onclick="reset()">Reset Position</button>