Skip to content

Instantly share code, notes, and snippets.

@sounisi5011
Last active February 5, 2019 04:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sounisi5011/2dcb436301a06b98ecc03c603f984dfb to your computer and use it in GitHub Desktop.
Save sounisi5011/2dcb436301a06b98ecc03c603f984dfb to your computer and use it in GitHub Desktop.
楕円の円周上の座標を求める
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1">
<meta name=format-detection content="telephone=no,email=no,address=no">
<title>楕円の円周の座標を求める</title>
<h1>楕円の円周の座標を求める</h1>
<p>
<input id=angle type=number value=0>°<br>
radiusX: <input id=rx type=number min=1 value=100><br>
radiusY: <input id=ry type=number min=1 value=50>
</p>
<svg viewBox="-300 -200 600 400" width="600" height="400">
<line id="line" x1="0" y1="0" x2="1000" y2="0" stroke="black"/>
<ellipse id="ellipse" cx="0" cy="0" stroke="blue" fill="none"/>
<circle id="point" r="4" stroke="red" fill="none"/>
</svg>
<script src="./main.js"></script>
/**
* 楕円の円周上の座標を返す
* @param {number} degree 角度
* @param {number} radiusX 横半径
* @param {number} radiusY 縦半径
* @returns {x: number, y: number} 円周上の座標
* @see https://qiita.com/yoshiweb/items/e88ad2f720d102d1103b#%E6%A5%95%E5%86%86%E3%81%AE%E5%86%86%E5%91%A8%E4%B8%8A%E3%81%AE-x-y-%E5%BA%A7%E6%A8%99%E3%82%92%E8%BF%94%E3%81%99
* @see https://open.mixi.jp/user/35955404/diary/1943102675
*/
function getOvalPosition(degree, radiusX, radiusY) {
const radian = deg2rad(degree);
/*
* Note: 90 < degree <= 270の範囲で座標が反転してしまう。原因は不明。
* TODO: 条件分岐が不要な式に直す
*/
const x = 1 / Math.sqrt(((1 / radiusX) ** 2) + ((Math.tan(radian) / radiusY) ** 2));
const y = x * Math.tan(radian);
return {
x: (90 < degree && degree <= 270) ? -x : x,
y: (90 < degree && degree <= 270) ? -y : y,
};
}
function deg2rad(degree) {
return normalizeDeg(degree) * (Math.PI / 180);
}
function rad2deg(radian) {
return normalizeDeg(radian * (180 / Math.PI));
}
function normalizeDeg(degree) {
return ((Number.parseFloat(degree) || 0) % 360 + 360) % 360;
}
function main() {
const angleInputElem = document.getElementById('angle');
const rxInputElem = document.getElementById('rx');
const ryInputElem = document.getElementById('ry');
const lineElem = document.getElementById('line');
const ellipseElem = document.getElementById('ellipse');
const pointElem = document.getElementById('point');
const state = {
angle: angleInputElem.value,
rx: rxInputElem.value,
ry: ryInputElem.value,
};
const render = (newState = state) => {
const data = Object.assign(state, newState);
const [degree, rx, ry] = [
normalizeDeg(data.angle),
Number.parseFloat(data.rx) || 1,
Number.parseFloat(data.ry) || 1,
];
const pointPos = getOvalPosition(
degree,
rx,
ry,
);
lineElem.setAttributeNS(null, 'transform', `rotate(${degree})`);
ellipseElem.setAttributeNS(null, 'rx', rx);
ellipseElem.setAttributeNS(null, 'ry', ry);
pointElem.setAttributeNS(null, 'cx', pointPos.x);
pointElem.setAttributeNS(null, 'cy', pointPos.y);
};
angleInputElem.addEventListener('input', event => {
render({
angle: event.currentTarget.value,
});
});
angleInputElem.addEventListener('blur', event => {
event.currentTarget.value = normalizeDeg(event.currentTarget.value);
});
rxInputElem.addEventListener('input', event => {
render({
rx: event.currentTarget.value,
});
});
ryInputElem.addEventListener('input', event => {
render({
ry: event.currentTarget.value,
});
});
render();
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment