Last active
January 2, 2022 07:34
-
-
Save motsu0/7a7c62fd9ddcb2be4c8244573c1a5a1d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#canvas{ | |
width: 100%; | |
border: 1px solid #ddd; | |
} | |
.sub-area{ | |
box-sizing: border-box; | |
margin: 20px 0; | |
border: 2px solid #aaa; | |
} | |
.sub-title{ | |
margin: 4px; | |
border-bottom: 1px solid #aaa; | |
text-align: center; | |
} | |
summary{ | |
display: block; | |
cursor: pointer; | |
user-select: none; | |
} | |
summary::before{ | |
content: "+"; | |
display: inline-flex; | |
justify-content: center; | |
align-items: center; | |
width: 20px; | |
} | |
details[open]>summary::before{ | |
content: "-"; | |
} | |
.sub-body{ | |
margin: 4px 8px; | |
} | |
.input-data{ | |
width: 60px; | |
margin: 0 4px; | |
} | |
.output,.output-title{ | |
border-color: #555; | |
} | |
.sel-data{ | |
margin: 0 4px; | |
cursor: pointer; | |
} | |
#bt-dl{ | |
display: block; | |
padding: 4px 8px; | |
margin: 20px auto; | |
cursor: pointer; | |
} | |
#bt-reset-pos{ | |
display: block; | |
margin-left: auto; | |
cursor: pointer; | |
} | |
/* */ | |
#msg-box{ | |
box-sizing: border-box; | |
width: 100%; | |
position: fixed; | |
bottom: 10px; | |
left: 0; | |
z-index: 1000; | |
text-align: center; | |
pointer-events: none; | |
} | |
#message{ | |
display: inline-block; | |
box-sizing: border-box; | |
max-width: 90%; | |
padding: 10px 20px; | |
border: 2px solid #BF360C; | |
background-color: #FBE9E7; | |
color: #BF360C; | |
word-break: break-all; | |
opacity: 1; | |
pointer-events: auto; | |
} | |
#message.off{ | |
opacity: 0; | |
transition: opacity 1s; | |
pointer-events: none; | |
} | |
.invisivle{ | |
visibility: hidden; | |
} | |
@media screen and (min-width:769px) { | |
#main-area{ | |
width: 60%; | |
margin: 0 auto; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="main-area"> | |
<div id="draw-box"> | |
<canvas id="canvas" width="600px" height="600px"></canvas> | |
</div> | |
<button id="bt-dl">画像をダウンロード</button> | |
<details class="input-area sub-area" open> | |
<summary class="input-title sub-title">設定</summary> | |
<div class="input-row sub-body"> | |
a:<input type="number" class="input-len input-data" min="1" placeholder="3" value="3"> | |
</div> | |
<div class="input-row sub-body"> | |
b:<input type="number" class="input-len input-data" min="1" placeholder="4" value="4"> | |
</div> | |
<div class="input-row sub-body"> | |
c:<input type="number" class="input-len input-data" min="1" placeholder="5" value="5"> | |
</div> | |
<div class="input-row sub-body"> | |
回転角度:<input type="number" id="input-angle" class="input-data" placeholder="0" value="0">° | |
</div> | |
<div class="input-row sub-body"> | |
背景色:<select id="sel-bg" class="sel-data"> | |
<option value="#fff">白色</option> | |
<option value="rgba(0,0,0,0)">透明</option> | |
</select> | |
</div> | |
<div class="input-row sub-body"> | |
<select id="sel-angle" class="sel-data"> | |
<option value="on">角の文字(ABC)を表示</option> | |
<option value="off">角の文字(ABC)を非表示</option> | |
</select> | |
</div> | |
<div class="input-row sub-body"> | |
<select id="sel-edge" class="sel-data"> | |
<option value="on">辺の文字(abc)を表示</option> | |
<option value="num">辺の値を表示</option> | |
<option value="off">辺の文字(abc)と値を非表示</option> | |
</select> | |
</div> | |
</details> | |
<details class="sub-area" open> | |
<summary class="sub-title">文字の位置調整</summary> | |
<div class="input-row sub-body"> | |
A | |
x:<input type="number" class="input-data input-pos-x" value="0" placeholder="0"> | |
y:<input type="number" class="input-data input-pos-y" value="0" placeholder="0"> | |
</div> | |
<div class="input-row sub-body"> | |
B | |
x:<input type="number" class="input-data input-pos-x" value="0" placeholder="0"> | |
y:<input type="number" class="input-data input-pos-y" value="0" placeholder="0"> | |
</div> | |
<div class="input-row sub-body"> | |
C | |
x:<input type="number" class="input-data input-pos-x" value="0" placeholder="0"> | |
y:<input type="number" class="input-data input-pos-y" value="0" placeholder="0"> | |
</div> | |
<div class="input-row sub-body"> | |
a | |
x:<input type="number" class="input-data input-pos-x" value="0" placeholder="0"> | |
y:<input type="number" class="input-data input-pos-y" value="0" placeholder="0"> | |
</div> | |
<div class="input-row sub-body"> | |
b | |
x:<input type="number" class="input-data input-pos-x" value="0" placeholder="0"> | |
y:<input type="number" class="input-data input-pos-y" value="0" placeholder="0"> | |
</div> | |
<div class="input-row sub-body"> | |
c | |
x:<input type="number" class="input-data input-pos-x" value="0" placeholder="0"> | |
y:<input type="number" class="input-data input-pos-y" value="0" placeholder="0"> | |
</div> | |
<div class="input-row sub-body"> | |
<button id="bt-reset-pos">リセット</button> | |
</div> | |
</details> | |
<div class="output sub-area"> | |
<div class="output-title sub-title"> | |
三角形の情報<br> | |
※各値は小数十一位を四捨五入したもの | |
</div> | |
<div id="output-body" class="sub-body"></div> | |
</div> | |
</div> | |
<div id="msg-box"> | |
<div id="message" class="off">データを保存しました。</div> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const input_data = document.getElementsByClassName('input-data'); | |
const input_len = document.getElementsByClassName('input-len'); | |
const input_angle = document.getElementById('input-angle'); | |
const sel_data = document.getElementsByClassName('sel-data'); | |
const sel_bg = document.getElementById('sel-bg'); | |
const sel_angle = document.getElementById('sel-angle'); | |
const sel_edge = document.getElementById('sel-edge'); | |
const input_pos_x = document.getElementsByClassName('input-pos-x'); | |
const input_pos_y = document.getElementsByClassName('input-pos-y'); | |
const bt_reset_pos = document.getElementById('bt-reset-pos'); | |
const bt_dl = document.getElementById('bt-dl'); | |
const output_body = document.getElementById('output-body'); | |
const canvas = document.getElementById('canvas'); | |
const ctx = canvas.getContext('2d'); | |
const fontsize = 30; | |
const delta_angle = 25; | |
const delta_edge = 20; | |
const can_wh = 500; | |
const can_margin = 50; | |
ctx.font = fontsize + 'px serif'; | |
ctx.textAlign = 'center'; | |
[...input_data].forEach(el=>{ | |
el.addEventListener('input',draw); | |
}); | |
[...sel_data].forEach(el=>{ | |
el.addEventListener('change',draw); | |
}); | |
bt_dl.addEventListener('click',download); | |
bt_reset_pos.addEventListener('click',resetPos); | |
draw(); | |
function draw(){ | |
//例外処理 | |
//辺の長さ取得 | |
const abc = [...input_len].map(el=>Number(el.value)); | |
for(let i=0;i<input_len.length;i++){ | |
if(input_len[i].value==''){ | |
renew(); | |
return; | |
} | |
} | |
//入力数値例外処理 | |
for(let i=0;i<=2;i++){ | |
if(isNaN(abc[i])||abc[i]<=0){ | |
renew(); | |
msgBox('入力値に誤りがあります。'); | |
return; | |
} | |
} | |
//位置調整取得 例外処理 | |
const pos_x = [...input_pos_x].map(el=>Number(el.value)); | |
const pos_y = [...input_pos_y].map(el=>Number(el.value)); | |
for(let i=0;i<pos_x.length;i++){ | |
if(isNaN(pos_x[i])||isNaN(pos_y[i])){ | |
renew(); | |
msgBox('入力値に誤りがあります。'); | |
return; | |
} | |
} | |
//回転角度取得 例外処理 | |
const degree = Number(input_angle.value); | |
if(isNaN(degree)){ | |
renew(); | |
msgBox('入力値に誤りがあります。'); | |
return; | |
} | |
const alpha = degree*Math.PI/180; | |
//三角形の成立判定 | |
const abc_sort = abc.slice(0).sort((a,b)=>a-b); | |
if(abc_sort[0]+abc_sort[1]<=abc_sort[2]){ | |
renew(); | |
msgBox('三角形の条件を満たしていません。'); | |
return; | |
} | |
//外接円半径Rと拡大率 | |
const R = can_wh/2; | |
const kariR = (abc[0]*abc[1]*abc[2]) / Math.sqrt((abc[0]+abc[1]+abc[2])*(-abc[0]+abc[1]+abc[2])*(abc[0]-abc[1]+abc[2])*(abc[0]+abc[1]-abc[2])); | |
const rate = R / kariR; | |
//abcの長さ | |
const a = abc[0] * rate; | |
const b = abc[1] * rate; | |
const c = abc[2] * rate; | |
//Aを原点とした点C、点P(外心)の座標 | |
const Bx = (-Math.pow(a,2)+Math.pow(b,2)+Math.pow(c,2)) / (2*b); | |
const By = Math.sqrt(Math.pow(c,2) - Math.pow(((-Math.pow(a,2)+Math.pow(b,2)+Math.pow(c,2)) / (2*b)),2)); | |
const Px = b/2; | |
const Py = Math.sqrt(Math.pow(R,2)-Math.pow(b,2)/4); | |
//平行移動後の点Pの座標 canvasの中心 | |
const Ox = can_wh/2 + can_margin; | |
const Oy = Ox; | |
//点Pをcanvas中心とした際の点A-Cの座標 | |
const kariA = [-Px, -Py]; | |
const kariB = [Bx-Px, By-Py]; | |
const kariC = [b-Px, -Py]; | |
//回転 | |
const rotA = rotate(kariA,alpha); | |
const rotB = rotate(kariB,alpha); | |
const rotC = rotate(kariC,alpha); | |
//座標軸を通常のxy軸からcanvas座標軸に変更 | |
const canA = [rotA[0],-rotA[1]]; | |
const canB = [rotB[0],-rotB[1]]; | |
const canC = [rotC[0],-rotC[1]]; | |
//点Pに合うように平行移動 | |
const A = [Ox+canA[0],Oy+canA[1]]; | |
const B = [Ox+canB[0],Oy+canB[1]]; | |
const C = [Ox+canC[0],Oy+canC[1]]; | |
//描画処理 | |
//初期化 | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
ctx.fillStyle = sel_bg.value; | |
ctx.fillRect(0, 0, canvas.width, canvas.height); | |
ctx.fillStyle = '#000'; | |
//内心を求める | |
const I = [(a*A[0]+b*B[0]+c*C[0])/(a+b+c),(a*A[1]+b*B[1]+c*C[1])/(a+b+c)]; | |
//角の文字の座標を求め、描画 | |
const letter_angle = ['A','B','C']; | |
const zahyou_angle = [A,B,C]; | |
if(sel_angle.value=='on'){ | |
for(let i=0;i<=2;i++){ | |
const sin = (zahyou_angle[i][1]-I[1])/(Math.sqrt(Math.pow(zahyou_angle[i][0]-I[0],2)+Math.pow(zahyou_angle[i][1]-I[1],2))); | |
const cos = (zahyou_angle[i][0]-I[0])/(Math.sqrt(Math.pow(zahyou_angle[i][0]-I[0],2)+Math.pow(zahyou_angle[i][1]-I[1],2))); | |
const zahyou = [zahyou_angle[i][0]+delta_angle*cos, zahyou_angle[i][1]+delta_angle*sin]; | |
const adjust = [pos_x[i], fontsize/2-pos_y[i]]; | |
ctx.fillText(letter_angle[i],zahyou[0]+adjust[0],zahyou[1]+adjust[1]); | |
} | |
} | |
//辺の文字の座標を求め、描画 | |
if(sel_edge.value=='on'||sel_edge.value=='num'){ | |
const letter_edge = (()=>{ | |
if(sel_edge.value=='on'){ | |
return ['a','b','c']; | |
}else{ | |
return abc.slice(0); | |
} | |
})(); | |
const zahyou_edge = [ | |
[(B[0]+C[0])/2,(B[1]+C[1])/2], | |
[(C[0]+A[0])/2,(C[1]+A[1])/2], | |
[(A[0]+B[0])/2,(A[1]+B[1])/2] | |
]; | |
const info_edge = [ | |
[(B[1]-C[1])/(B[0]-C[0]), B], | |
[(C[1]-A[1])/(C[0]-A[0]), C], | |
[(A[1]-B[1])/(A[0]-B[0]), A] | |
]; | |
for(let i=0;i<=2;i++){ | |
const hosei = new Array(2); | |
const atan = Math.atan(info_edge[i][0]); | |
hosei[0] = delta_edge * Math.cos(atan-Math.PI/2); | |
hosei[1] = delta_edge * Math.sin(atan-Math.PI/2); | |
const dist = info_edge[i][0]*(zahyou_angle[i][0] - info_edge[i][1][0]) + info_edge[i][1][1]; | |
if(dist>zahyou_angle[i][1]){ | |
hosei[0] *= -1; | |
hosei[1] *= -1; | |
} | |
const zahyou = [zahyou_edge[i][0]+hosei[0], zahyou_edge[i][1]+hosei[1]]; | |
const hosei_fs_x = (()=>{ | |
if(sel_edge.value!='num'){ | |
return 0 | |
}else if(hosei[0]>=0){ | |
return (String(abc[i]).length-1)*fontsize*.5*.5; | |
}else{ | |
return -(String(abc[i]).length-1)*fontsize*.5*.5; | |
} | |
})(); | |
const adjust = [hosei_fs_x+pos_x[i+3], fontsize*.5-pos_y[i+3]]; | |
ctx.fillText(letter_edge[i],zahyou[0]+adjust[0],zahyou[1]+adjust[1]); | |
} | |
} | |
//三角形を描画 | |
ctx.beginPath(); | |
ctx.moveTo(A[0],A[1]); | |
ctx.lineTo(B[0],B[1]); | |
ctx.lineTo(C[0],C[1]); | |
ctx.lineTo(A[0],A[1]); | |
ctx.stroke(); | |
//三角形の情報 | |
const info = []; | |
info.push(`a:${abc[0]}`); | |
info.push(`b:${abc[1]}`); | |
info.push(`c:${abc[2]}`); | |
info.push(`回転角度:${Number(input_angle.value)}°`); | |
const cosA = (Math.pow(abc[1],2)+Math.pow(abc[2],2)-Math.pow(abc[0],2)) / (2*abc[1]*abc[2]); | |
const cosB = (Math.pow(abc[2],2)+Math.pow(abc[0],2)-Math.pow(abc[1],2)) / (2*abc[2]*abc[0]); | |
const cosC = (Math.pow(abc[0],2)+Math.pow(abc[1],2)-Math.pow(abc[2],2)) / (2*abc[0]*abc[1]); | |
info.push(`cosA:${round(cosA)}`); | |
info.push(`cosB:${round(cosB)}`); | |
info.push(`cosC:${round(cosC)}`); | |
info.push(`sinA:${round(Math.sqrt(1-Math.pow(cosA,2)))}`); | |
info.push(`sinB:${round(Math.sqrt(1-Math.pow(cosB,2)))}`); | |
info.push(`sinC:${round(Math.sqrt(1-Math.pow(cosC,2)))}`); | |
const area = Math.sqrt(((abc[0]+abc[1]+abc[2])/2)*((abc[0]+abc[1]+abc[2])/2-abc[0])*((abc[0]+abc[1]+abc[2])/2-abc[1])*((abc[0]+abc[1]+abc[2])/2-abc[2])); | |
info.push(`面積:${round(area)}`); | |
output_body.innerHTML = info.join('<br>'); | |
//処理終了 | |
function rotate(origin,angle){ | |
const x = R * ( (origin[0]/R)*Math.cos(angle) + (origin[1]/R)*Math.sin(angle) ); | |
const y = R * ( (origin[1]/R)*Math.cos(angle) - (origin[0]/R)*Math.sin(angle) ); | |
return [x,y]; | |
} | |
function renew(){ | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
output_body.textContent = ''; | |
} | |
} | |
function download(){ | |
const alink = document.createElement('a'); | |
alink.download = 'triangle.png'; | |
canvas.toBlob(blob=>{ | |
alink.href = window.URL.createObjectURL(blob); | |
alink.click(); | |
},'image/png'); | |
} | |
function resetPos(){ | |
[...input_pos_x].forEach(el=>{ | |
el.value = 0; | |
}); | |
[...input_pos_y].forEach(el=>{ | |
el.value = 0; | |
}); | |
draw(); | |
} | |
function round(n){ | |
return Math.round(n*Math.pow(10,10)) / Math.pow(10,10); | |
} | |
function msgBox(str){ | |
message.textContent = str; | |
message.classList.remove('off'); | |
setTimeout(()=>{ | |
message.classList.add('off'); | |
},1000); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment