歯車について勉強する6 の履歴(No.1)
更新LANG: p5js_live
// src/css-text.ts
function generateRandomId(length) {
let result = "";
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const charactersLength = characters.length;
let counter = 0;
while (counter < length) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
counter += 1;
}
return result;
}
var cssText = (id) => `
@media screen and (min-width: 640px) {
#${id} {
position: absolute;
left: 0;
top: 0;
opacity: 0.2;
}
#${id}:hover {
opacity: 0.7;
}
#${id} .control-slider {
padding-left: 10px;
}
}
@media screen and (max-width: 639px) {
#${id} .check-hide-tools {
display: none !important;
}
#${id} {
height: 30vh;
overflow-y: scroll;
line-height: 2;
}
#${id} > :last-child {
padding-bottom: 10vh;
}
#${id} .control-checkbox {
width:85px !important;
}
#${id} .control-slider input {
width: 200px !important;
}
}
#${id} {
display: block;
max-width: 500px
}
#${id} .control-slider {
display: block
}
#${id} .control-slider :first-child {
display: inline-block;
width: 80px
}
#${id} .control-slider :nth-child(2) {
display: inline-block;
width: 50px
}
#${id} .control-slider input {
display: inline-block;
width: 350px;
}
#${id} .control-checkbox {
display:inline-block;
width:110px;
overflow:hidden
}
`;
// src/initializeControls.ts
function initializeControls(parent, inputChanged = () => {
}) {
const id = generateRandomId(8);
const wrapper = elem("div", "", [elem("style", cssText(id))], { id });
parent.appendChild(wrapper);
const wrapControls = elem("div", "", [], {});
const checkHide = createCheckbox(wrapper, "ツールを非表示", () => {
wrapControls.style.display = checkHide.checked ? "none" : "block";
});
checkHide.parentElement.classList.add("check-hide-tools");
wrapper.appendChild(wrapControls);
const controls = {
alpha: createSlider(
wrapControls,
"圧力角",
{ min: 10, max: 32, value: 20, step: 1 },
inputChanged
),
z1: createSlider(wrapControls, "歯数1", { min: 4, max: 200, value: 20, step: 1 }, inputChanged),
z2: createSlider(wrapControls, "歯数2", { min: 4, max: 200, value: 30, step: 1 }, inputChanged),
axesAngle: createSlider(
wrapControls,
"軸角度",
{ min: 20, max: 160, value: 90, step: 5 },
inputChanged
),
theta: createSlider(wrapControls, "θ", { min: -5, max: 5, value: 0, step: 0.02 }, inputChanged),
stop: false
};
controls.theta.addEventListener("mousedown", () => {
controls.stop = true;
});
controls.theta.addEventListener("mouseup", () => {
controls.stop = false;
});
return {
get z1() {
return Number(controls.z1.value);
},
get z2() {
return Number(controls.z2.value);
},
get alpha() {
return Number(controls.alpha.value) / 180 * Math.PI;
},
get fillet() {
return 0.4;
},
get mk() {
return 1;
},
get mf() {
return 1.25;
},
get axesAngle() {
return Number(controls.axesAngle.value);
},
get theta() {
return Number(controls.theta.value);
},
set theta(value) {
controls.theta.value = value.toPrecision(4);
controls.theta.doInput();
},
incrementTheta() {
const delta = 0.01;
if (controls.stop) return;
if (this.theta + delta >= Number(controls.theta.max)) {
this.theta = this.theta + delta - Number(controls.theta.max) + Number(controls.theta.min);
} else {
this.theta += delta;
}
}
};
}
function elem(tag, html = "", children = [], props = {}, events = {}) {
const result = document.createElement(tag);
if (html != "") result.innerHTML = html;
children.forEach((c) => result.appendChild(c));
Object.entries(props).sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0).forEach(([k, v]) => {
if (k == "class") {
result.className = String(v);
} else if (typeof v == "object") {
Object.entries(v).forEach(([k2, v2]) => result[k][k2] = v2);
} else {
result[k] = v;
}
});
const notIsArray = (maybeArray) => {
return !Array.isArray(maybeArray);
};
Object.entries(events).forEach(([k, v]) => {
if (notIsArray(v)) v = [v];
v.forEach((f) => result.addEventListener(k, f));
});
return result;
}
function createSlider(parent, label, option, oninput) {
const value = elem("span", String(option.value));
const slider = elem(
"input",
"",
[],
{ type: "range", ...option },
{ input: () => slider.doInput() }
);
slider.doInput = () => {
value.innerHTML = slider.value;
oninput();
};
const wrapper = elem("div", "", [elem("span", label), value, slider], {
class: "control-slider"
});
parent.appendChild(wrapper);
return slider;
}
function createCheckbox(parent, label, oninput, checked = false) {
const check = elem(
"input",
"",
[],
{ type: "checkbox", checked },
{ input: (e) => check.doInput(e) }
);
check.doInput = oninput;
const wrapper = elem("label", "", [check, elem("span", label)], {
class: "control-checkbox"
});
parent.appendChild(wrapper);
return check;
}
// src/vector3d.ts
var Vector3D = class _Vector3D {
x;
y;
z;
constructor(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
static polar(r, theta, phi) {
return new _Vector3D(
r * Math.sin(phi) * Math.cos(theta),
r * Math.sin(phi) * Math.sin(theta),
r * Math.cos(phi)
);
}
angle() {
return Math.atan2(this.y, this.x);
}
equal(v) {
const epsilon = 1e-6;
return Math.abs(this.x - v.x) < epsilon && Math.abs(this.y - v.y) < epsilon && Math.abs(this.z - v.z) < epsilon;
}
add(v) {
return new _Vector3D(this.x + v.x, this.y + v.y, this.z + v.z);
}
sub(v) {
return new _Vector3D(this.x - v.x, this.y - v.y, this.z - v.z);
}
mul(scalar) {
return new _Vector3D(this.x * scalar, this.y * scalar, this.z * scalar);
}
rotate(axis, angle) {
axis = axis.normalize();
const cos2 = Math.cos(angle);
const sin2 = Math.sin(angle);
const { x, y, z } = this;
const { x: ux, y: uy, z: uz } = axis;
const dot = this.inner(axis);
const cross = this.outer(axis);
return new _Vector3D(
x * cos2 + cross.x * sin2 + ux * dot * (1 - cos2),
y * cos2 + cross.y * sin2 + uy * dot * (1 - cos2),
z * cos2 + cross.z * sin2 + uz * dot * (1 - cos2)
);
}
flipY() {
return new _Vector3D(this.x, -this.y, this.z);
}
flipX() {
return new _Vector3D(-this.x, this.y, this.z);
}
flipZ() {
return new _Vector3D(this.x, this.y, -this.z);
}
norm() {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
normalize(length = 1) {
const len = this.norm() / length;
return new _Vector3D(this.x / len, this.y / len, this.z / len);
}
polar() {
return [this.norm(), this.angle()];
}
inner(v) {
return this.x * v.x + this.y * v.y + this.z * v.z;
}
outer(v) {
return new _Vector3D(
this.y * v.z - this.z * v.y,
this.z * v.x - this.x * v.z,
this.x * v.y - this.y * v.x
);
}
cross(v) {
return this.outer(v);
}
distance(v) {
return v.sub(this).norm();
}
normalVectors() {
const x = this.x;
const y = this.y;
const z = this.z;
if (Math.abs(x) > Math.abs(y)) {
return [new _Vector3D(-z, 0, x).normalize(), new _Vector3D(0, -z, y).normalize()];
} else {
return [new _Vector3D(0, -z, y).normalize(), new _Vector3D(-z, 0, x).normalize()];
}
}
};
// src/main.ts
var { max, sin, cos, tan, asin, acos, atan, atan2, PI } = Math;
var cameraP = new Vector3D(3, 0, 0);
var cameraU = new Vector3D(0, -1, 0);
var cameraV = new Vector3D(0, 0, 1);
var cameraW = PI / 8;
var sketch = (p2) => {
let params;
const canvas = {
width: 800,
height: 800,
ox: 0,
oy: 0
};
p2.mouseDragged = (e) => {
if (e.target.tagName !== "CANVAS") return;
cameraP = cameraP.sub(cameraU.mul(2 * (p2.mouseX - p2.pmouseX) * (cameraP.norm() - 1) / canvas.width)).normalize(cameraP.norm());
cameraU = cameraP.outer(cameraV).normalize();
cameraP = cameraP.sub(cameraV.mul(2 * (p2.mouseY - p2.pmouseY) * (cameraP.norm() - 1) / canvas.width)).normalize(cameraP.norm());
cameraV = cameraP.outer(cameraU).normalize(-1);
inputChanged();
return true;
};
p2.mouseWheel = (event) => {
const delta = event.deltaY / 200;
let n = cameraP.norm() * (1 + delta);
n = Math.max(1.1, n);
cameraP = cameraP.normalize(n);
inputChanged();
return true;
};
p2.setup = () => {
const { width, height } = canvas;
canvas.ox = width / 2;
canvas.oy = height * 2 / 3;
const c = p2.createCanvas(width, height);
c.style("max-width", "100%");
c.style("height", "auto");
const wrapper = c.elt.parentElement;
params = initializeControls(wrapper, inputChanged);
inputChanged();
setInterval(() => {
params.incrementTheta();
}, 100);
};
const inputChanged = () => {
const { width, height } = canvas;
const sigma = params.axesAngle * PI / 180;
const gammaP1 = atan2(sin(sigma), params.z2 / params.z1 + cos(sigma));
const gammaP2 = sigma - gammaP1;
const r = params.z1 / (2 * sin(gammaP1));
const rm = 1 / r;
const axis1 = new Vector3D(cos(gammaP1), 0, sin(gammaP1));
const axis2 = new Vector3D(cos(gammaP2), 0, -sin(gammaP2));
const eps = 1e-5;
function proj(lambdaOrVec, phi = 0) {
if (typeof lambdaOrVec === "number") {
lambdaOrVec = new Vector3D(
cos(phi) * cos(lambdaOrVec),
sin(phi) * cos(lambdaOrVec),
sin(lambdaOrVec)
);
} else {
lambdaOrVec = lambdaOrVec.normalize();
}
const p3 = lambdaOrVec.sub(cameraP);
if (p3.inner(lambdaOrVec) > 0) return [NaN, NaN];
const u = p3.inner(cameraU);
const v = p3.inner(cameraV);
const d = -p3.inner(cameraP.normalize());
return [(1 + atan2(u, d) / cameraW) * (width / 2), (1 + atan2(v, d) / cameraW) * (width / 2)];
}
function isArrayOf(obj, predicate) {
return Array.isArray(obj) && obj.every(predicate);
}
function drawCurve(points) {
if (points.length === 0) return;
if (isArrayOf(points, (item) => item instanceof Vector3D)) {
points = points.map((p3) => proj(p3));
}
let last = [NaN, NaN];
points.forEach((point) => {
if (isNaN(point[0])) {
if (!isNaN(last[0])) {
p2.curveVertex(...last);
p2.endShape();
}
last = point;
return;
}
if (isNaN(last[0])) {
p2.beginShape();
p2.curveVertex(...point);
}
p2.curveVertex(...point);
last = point;
});
if (!isNaN(last[0])) {
p2.curveVertex(...last);
p2.endShape();
}
}
const initializeCanvas = () => {
p2.fill(220);
p2.rect(0, 0, width, height);
p2.stroke(100);
for (let j = 0; j <= 18; j++) {
const phi = j * PI / 18 - PI / 2;
const points = [];
for (let lambda = -PI; lambda <= PI; lambda += PI / 60 - eps) {
points.push(proj(phi, lambda));
}
if (j % 9 === 0) {
p2.stroke(160);
} else {
p2.stroke(200);
}
p2.noFill();
drawCurve(points);
}
for (let i = 0; i <= 36; i++) {
const lambda = i * PI / 18;
const points = [];
for (let phi = -PI / 2; phi <= PI / 2; phi += PI / 30 - eps) {
const pp = proj(phi, lambda);
points.push(pp);
}
if (i % 9 === 0) {
p2.stroke(160);
} else {
p2.stroke(200);
}
p2.noFill();
drawCurve(points);
}
(() => {
p2.drawingContext.setLineDash([10, 10]);
const points = [];
const a = new Vector3D(0, cos(params.alpha), -sin(params.alpha));
const v = new Vector3D(1, 0, 0);
for (let i = 0; i <= 20; i++) {
points.push(proj(v.rotate(a, (-0.1 + 0.2 * i / 20) * PI)));
}
drawCurve(points);
p2.drawingContext.setLineDash([]);
})();
(() => {
p2.drawingContext.setLineDash([10, 10]);
const points = [];
const a = new Vector3D(0, sin(params.alpha), cos(params.alpha));
const v = new Vector3D(1, 0, 0);
for (let i = 0; i <= 20; i++) {
points.push(proj(v.rotate(a, (-0.1 + 0.2 * i / 20) * PI)));
}
drawCurve(points);
p2.drawingContext.setLineDash([]);
})();
};
initializeCanvas();
function drawCircle(axis, delta) {
const [axis1u, axis1v] = axis.normalVectors();
const points = [];
const n = 36;
for (let i = 0; i <= 2 * n; i++) {
const t = i * PI / n;
const p12 = axis.add(axis1u.mul(tan(delta) * cos(t))).add(axis1v.mul(tan(delta) * sin(t)));
points.push(proj(p12));
}
drawCurve(points);
}
p2.stroke(0);
p2.strokeWeight(2);
p2.circle(...proj(axis1), 2);
p2.circle(...proj(axis2), 2);
const gammaF1 = gammaP1 - params.mf * atan(rm);
const gammaK1 = gammaP1 + params.mk * atan(rm);
const gammaT1 = gammaP1 + params.mf * atan(rm);
const gammaF2 = gammaP2 - params.mf * atan(rm);
const gammaK2 = gammaP2 + params.mk * atan(rm);
const gammaT2 = gammaP2 + params.mf * atan(rm);
p2.stroke(160);
p2.strokeWeight(1);
p2.noFill();
p2.drawingContext.setLineDash([10, 10]);
drawCircle(axis1, gammaP1);
drawCircle(axis1, gammaF1);
drawCircle(axis1, gammaK1);
drawCircle(axis1, gammaT1);
drawCircle(axis2, gammaP2);
drawCircle(axis2, gammaF2);
drawCircle(axis2, gammaK2);
drawCircle(axis2, gammaT2);
p2.drawingContext.setLineDash([]);
const gammaB1 = asin(cos(params.alpha) * sin(gammaP1));
const gammaB2 = asin(cos(params.alpha) * sin(gammaP2));
drawCircle(axis1, gammaB1);
drawCircle(axis2, gammaB2);
function trans(epsilon, v, axis, gammaB) {
axis = axis.normalize(cos(gammaB));
v = v.normalize();
const oq = v.sub(axis);
const om = oq.rotate(axis, epsilon);
const oom = axis.add(om);
const axis22 = oom.sub(axis.normalize(1 / cos(gammaB)));
const op = oom.rotate(axis22, epsilon * sin(gammaB));
return op;
}
function sphericalInvolute(axis, v, gammaB, gammaF, gammaK) {
const points = [];
for (let i = 0; i <= 20; i++) {
const t = gamma2epsilon(gammaF, gammaB) + (gamma2epsilon(gammaK, gammaB) - gamma2epsilon(gammaF, gammaB)) * i / 20;
points.push(trans(t, v, axis, gammaB));
}
return points;
}
function gamma2theta(gamma, gammaB) {
const varphi = acos(cos(gamma) / cos(gammaB));
return varphi / sin(gammaB) - atan2(tan(varphi), sin(gammaB));
}
function gamma2epsilon(gamma, gammaB) {
const varphi = acos(cos(gamma) / cos(gammaB));
return varphi / sin(gammaB);
}
function drawArc(axis, p12, p2Angle, options = {}) {
const { greatCircle = false, n = 6 } = options;
const c = [];
if (greatCircle) {
const v1 = p12;
const v2 = typeof p2Angle === "number" ? v1.rotate(axis, p2Angle) : p2Angle;
const t = acos(v1.inner(v2) / (v1.norm() * v2.norm()));
for (let j = 0; j <= n; j++) {
const t2 = t * j / n;
c.push(v1.rotate(axis, t2));
}
} else {
const v1 = p12.sub(axis);
const v2 = typeof p2Angle === "number" ? v1.rotate(axis, p2Angle) : p2Angle.sub(axis);
const t = acos(v1.inner(v2) / (v1.norm() * v2.norm()));
for (let j = 0; j <= n; j++) {
const t2 = t * j / n;
c.push(axis.add(v1.rotate(axis, t2)));
}
}
drawCurve(c);
}
p2.stroke(0);
const p1 = new Vector3D(cos(gammaP1 - gammaB1), 0, sin(gammaP1 - gammaB1)).rotate(
axis1,
-gamma2theta(gammaP1, gammaB1)
);
const curve1 = sphericalInvolute(axis1, p1, gammaB1, max(gammaB1, gammaF1), gammaK1);
for (let i = 0; i < params.z1; i++) {
const c1 = curve1.map((p3) => p3.rotate(axis1, i * 2 * PI / params.z1)).map(
(p3) => p3.rotate(axis1, 2 * PI / params.z1 * params.theta)
);
const c2 = curve1.map((p3) => p3.flipY().rotate(axis1, (i + 1 / 2) * 2 * PI / params.z1)).map(
(p3) => p3.rotate(axis1, 2 * PI / params.z1 * params.theta)
);
drawCurve(c1);
drawCurve(c2);
drawArc(axis1.normalize(cos(gammaK1)), c1.at(-1), c2.at(-1));
if (gammaB1 > gammaF1) {
const a1 = axis1.cross(c1.at(0));
drawArc(a1, c1.at(0), gammaB1 - gammaF1, { greatCircle: true });
const a2 = axis1.cross(c2.at(0));
drawArc(a2, c2.at(0), gammaB1 - gammaF1, { greatCircle: true });
drawArc(
axis1.normalize(cos(gammaF1)),
c2.at(0).rotate(a2, gammaB1 - gammaF1).rotate(axis1, -2 * PI / params.z1),
c1.at(0).rotate(a1, gammaB1 - gammaF1)
);
} else {
drawArc(
axis1.normalize(cos(gammaF1)),
c2.at(0).rotate(axis1, -2 * PI / params.z1),
c1.at(0)
);
}
}
const p22 = new Vector3D(cos(gammaP2 - gammaB2), 0, -sin(gammaP2 - gammaB2)).rotate(
axis2,
-gamma2theta(gammaP2, gammaB2)
);
const curve2 = sphericalInvolute(axis2, p22, gammaB2, max(gammaB2, gammaF2), gammaK2);
for (let i = 0; i < params.z2; i++) {
const c1 = curve2.map((p3) => p3.rotate(axis2, i * 2 * PI / params.z2)).map(
(p3) => p3.rotate(axis2, -2 * PI / params.z2 * params.theta)
);
const c2 = curve2.map((p3) => p3.flipY().rotate(axis2, (i + 1 / 2) * 2 * PI / params.z2)).map(
(p3) => p3.rotate(axis2, -2 * PI / params.z2 * params.theta)
);
drawCurve(c1);
drawCurve(c2);
drawArc(axis2.normalize(cos(gammaK2)), c1.at(-1), c2.at(-1));
if (gammaB2 > gammaF2) {
const a1 = axis2.cross(c1.at(0));
drawArc(a1, c1.at(0), gammaB2 - gammaF2, { greatCircle: true });
const a2 = axis2.cross(c2.at(0));
drawArc(a2, c2.at(0), gammaB2 - gammaF2, { greatCircle: true });
drawArc(
axis2.normalize(cos(gammaF2)),
c2.at(0).rotate(a2, gammaB2 - gammaF2).rotate(axis2, -2 * PI / params.z2),
c1.at(0).rotate(a1, gammaB2 - gammaF2)
);
} else {
drawArc(
axis2.normalize(cos(gammaF2)),
c2.at(0).rotate(axis2, -2 * PI / params.z2),
c1.at(0)
);
}
}
return;
};
Counter: 669 (from 2010/06/03),
today: 1,
yesterday: 0