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>
|