Add R2C drawing 3D viewer
This commit is contained in:
1 parent
b59196feb3
commit
277c7005ab
2 files changed
+109
-1
No files matched your search
@@ -1 +1,5 @@
|
||||
# 3D practice
|
||||
# R2C drawing viewer
|
||||
|
||||
Open `index.html` in a browser to view the interactive 3D schematic. Drag to rotate, use the mouse wheel to zoom, switch between standard views, and adjust the displayed model depth. The “원본 도면 열기” link opens the PDF in the browser.
|
||||
|
||||
The schematic reflects the drawing's 2,500 × 1,620 mm outer dimensions and 120 mm thickness callout. The drawing does not provide a clearly readable extrusion depth in the extracted dimensions, so the viewer exposes that value as an adjustable visualization setting.
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
<!doctype html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#101b2a">
|
||||
<title>R2C 일반도 · 3D 뷰어</title>
|
||||
<style>
|
||||
:root{color-scheme:dark;--bg:#101a28;--panel:#172435;--panel2:#1b2b3e;--line:#2d4055;--muted:#95a7b9;--text:#edf3f8;--cyan:#65d3d0;--amber:#f1bd70;--red:#ff8278}
|
||||
*{box-sizing:border-box}html,body{margin:0;min-height:100%;background:var(--bg);color:var(--text);font:14px/1.5 Inter,"Noto Sans KR",system-ui,-apple-system,"Segoe UI",sans-serif}button,a,input{font:inherit}button{color:inherit}
|
||||
.app{min-height:100vh;display:grid;grid-template-rows:68px minmax(0,1fr);padding:0 26px 24px;max-width:1680px;margin:auto}.topbar{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--line);gap:16px}.brand{display:flex;align-items:center;gap:12px}.mark{height:34px;width:34px;border:1px solid #47707a;border-radius:10px;display:grid;place-items:center;color:var(--cyan);font-weight:800;letter-spacing:-1px;background:linear-gradient(140deg,#173d4b,#172537)}.brand-title{font-weight:700;letter-spacing:.01em}.brand-sub{color:var(--muted);font-size:11px;margin-top:1px}.top-actions{display:flex;align-items:center;gap:9px}.pill{border:1px solid var(--line);border-radius:99px;padding:6px 11px;color:#c8d5df;font-size:11px;white-space:nowrap}.pill .dot{display:inline-block;width:6px;height:6px;background:#65d3a2;border-radius:50%;margin:0 7px 1px 1px}.pdf-link{display:flex;align-items:center;gap:7px;color:var(--text);text-decoration:none;padding:8px 12px;border-radius:8px;border:1px solid var(--line);background:var(--panel);font-size:12px}.pdf-link:hover{border-color:#5b758d;background:var(--panel2)}
|
||||
main{display:grid;grid-template-columns:minmax(0,1fr) 314px;gap:18px;padding-top:18px;min-height:0}.stage-wrap{min-width:0;display:grid;grid-template-rows:auto minmax(400px,1fr) auto;gap:12px}.stage-heading{display:flex;align-items:flex-end;justify-content:space-between;gap:20px}.eyebrow{font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--cyan);font-weight:700}.stage-heading h1{font-size:20px;line-height:1.25;margin:3px 0 0;letter-spacing:-.025em}.stage-note{font-size:11px;color:var(--muted);text-align:right}.canvas-shell{position:relative;border:1px solid #2b3e51;border-radius:12px;overflow:hidden;min-height:400px;background:#e7edf0;box-shadow:0 20px 60px #0002}.canvas-shell:after{content:"";position:absolute;inset:0;pointer-events:none;background:linear-gradient(180deg,#fff2,transparent 18%,transparent 78%,#23394d12)}canvas{display:block;width:100%;height:100%;touch-action:none;cursor:grab}canvas:active{cursor:grabbing}.canvas-hud{position:absolute;z-index:1;left:16px;top:14px;display:flex;gap:7px;pointer-events:none}.hud-tag{background:#f7fafbe8;border:1px solid #cfdae0;color:#415365;padding:5px 8px;border-radius:6px;font-size:10px;font-weight:650}.canvas-hint{position:absolute;z-index:1;right:16px;bottom:14px;color:#506376;font-size:10px;background:#f7fafbe8;border:1px solid #cfdae0;border-radius:6px;padding:5px 8px;pointer-events:none}.viewbar{display:flex;justify-content:space-between;align-items:center;gap:12px}.views{display:flex;gap:6px;flex-wrap:wrap}.view-btn,.tool-btn{border:1px solid var(--line);background:var(--panel);border-radius:7px;padding:7px 11px;font-size:11px;color:#bfccd7;cursor:pointer}.view-btn:hover,.tool-btn:hover{background:var(--panel2)}.view-btn.active{background:#17464b;border-color:#337b7b;color:#baf0e9}.tools{display:flex;gap:6px;align-items:center}.tool-btn{display:flex;align-items:center;gap:6px}.tool-btn.active{border-color:#397d79;color:#9ce4dc;background:#163c43}.tool-btn svg,.pdf-link svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
|
||||
aside{display:flex;flex-direction:column;gap:12px;min-width:0}.card{border:1px solid var(--line);background:var(--panel);border-radius:10px;padding:15px}.card-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}.card-title{font-weight:680;font-size:12px}.card-kicker{color:var(--muted);font-size:10px}.status{color:var(--cyan);font-size:10px;background:#15383c;border:1px solid #255456;padding:3px 7px;border-radius:99px}.dims{display:grid;gap:7px}.dim-row{display:flex;justify-content:space-between;align-items:center;color:#c3d0da;font-size:11px}.dim-row strong{font-size:12px;color:#f2f6f8;font-variant-numeric:tabular-nums}.dim-row span{color:var(--muted)}.dim-row .guess{color:var(--amber)}.divider{height:1px;background:var(--line);margin:5px 0}.range-wrap{margin-top:13px}.range-top{display:flex;justify-content:space-between;color:#c3d0da;font-size:10px;margin-bottom:6px}.range-top output{color:var(--cyan);font-variant-numeric:tabular-nums}input[type=range]{appearance:none;width:100%;height:3px;background:#3a4b5f;border-radius:5px;outline:none}input[type=range]::-webkit-slider-thumb{appearance:none;width:13px;height:13px;border-radius:50%;background:var(--cyan);border:2px solid #163541;cursor:pointer}input[type=range]::-moz-range-thumb{width:11px;height:11px;border-radius:50%;background:var(--cyan);border:2px solid #163541;cursor:pointer}.materials{display:grid;gap:9px}.mat-row{display:flex;align-items:center;gap:9px;font-size:11px;color:#c6d3dd}.swatch{width:9px;height:9px;border-radius:3px;background:#bbc4c8;border:1px solid #d5dcde}.swatch.rebar{background:#e47a69;border-color:#f59c89}.swatch.dimline{background:#d69b4d;border-color:#f5cb7b}.mat-row small{margin-left:auto;color:var(--muted);font-size:10px}.info{background:#202d3a;border:1px solid #3e4b53}.info .card-title{display:flex;align-items:center;gap:7px}.info .icon{width:17px;height:17px;display:grid;place-items:center;border-radius:50%;font-size:11px;color:#2f2412;background:#eab969;font-weight:800}.info p{margin:0;color:#bdc9d2;font-size:10px;line-height:1.65}.info strong{color:#f1d29c;font-weight:650}.file-name{font-size:10px;color:#aab9c6;overflow-wrap:anywhere;margin-top:9px;padding-top:9px;border-top:1px solid #354454}.footer-note{color:#71869a;font-size:10px;margin:0 2px}.toast{position:absolute;left:50%;top:20px;transform:translate(-50%,-8px);opacity:0;transition:.2s;background:#142735;color:#bceee8;border:1px solid #38716e;border-radius:7px;padding:7px 11px;font-size:11px;pointer-events:none;z-index:3}.toast.show{opacity:1;transform:translate(-50%,0)}
|
||||
@media(max-width:950px){.app{padding:0 16px 18px}main{grid-template-columns:minmax(0,1fr) 270px;gap:12px}.stage-wrap{grid-template-rows:auto minmax(360px,1fr) auto}.canvas-shell{min-height:360px}}
|
||||
@media(max-width:720px){.app{grid-template-rows:60px 1fr;padding:0 12px 14px}.brand-sub,.top-actions .pill{display:none}main{grid-template-columns:1fr;padding-top:13px}.stage-wrap{grid-template-rows:auto minmax(330px,55vh) auto}.canvas-shell{min-height:330px}.stage-heading h1{font-size:17px}.stage-note{font-size:10px;max-width:130px}.viewbar{align-items:flex-start;flex-direction:column}.tools{width:100%}aside{display:grid;grid-template-columns:1fr 1fr}.card.info{grid-column:1/-1}.card{padding:12px}.topbar{height:60px}}
|
||||
@media(max-width:430px){aside{grid-template-columns:1fr}.card.info{grid-column:auto}.canvas-hint{right:9px;bottom:9px}.canvas-hud{left:9px;top:9px}.app{padding-inline:9px}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
<header class="topbar">
|
||||
<div class="brand"><div class="mark">R2</div><div><div class="brand-title">도면 3D 뷰어</div><div class="brand-sub">R2C · 일반도 기반 형상 검토</div></div></div>
|
||||
<div class="top-actions"><span class="pill"><i class="dot"></i>로컬 인터랙티브 모델</span><a class="pdf-link" href="./drawing/R2C_일반도_20260601.pdf" target="_blank" rel="noopener"><svg viewBox="0 0 24 24"><path d="M14 3h7v7M10 14 21 3M19 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h6"/></svg>원본 도면 열기</a></div>
|
||||
</header>
|
||||
<main>
|
||||
<section class="stage-wrap">
|
||||
<div class="stage-heading"><div><div class="eyebrow">STRUCTURAL MODEL / R2C</div><h1>R2C 일반도 3D 형상</h1></div><div class="stage-note">마우스로 회전 · 휠로 확대/축소</div></div>
|
||||
<div class="canvas-shell"><canvas id="scene" aria-label="R2C 도면 기반 3D 모델"></canvas><div class="canvas-hud"><span class="hud-tag">RC BOX · SCHEMATIC</span><span class="hud-tag">단위 mm</span></div><div class="canvas-hint">드래그하여 모델을 회전하세요</div><div class="toast" id="toast"></div></div>
|
||||
<div class="viewbar"><div class="views" role="group" aria-label="보기 방향"><button class="view-btn active" data-view="iso">등각</button><button class="view-btn" data-view="front">정면</button><button class="view-btn" data-view="side">측면</button><button class="view-btn" data-view="top">평면</button></div><div class="tools"><button class="tool-btn active" id="rebarBtn" aria-pressed="true"><svg viewBox="0 0 24 24"><path d="M5 19 19 5M4 12l8-8M12 20l8-8M4 17l3 3M17 4l3 3"/></svg>철근 표시</button><button class="tool-btn" id="resetBtn"><svg viewBox="0 0 24 24"><path d="M3 12a9 9 0 1 0 2.6-6.4L3 8M3 3v5h5"/></svg>초기화</button></div></div>
|
||||
</section>
|
||||
<aside>
|
||||
<section class="card"><div class="card-head"><span class="card-title">주요 치수</span><span class="status">도면 확인</span></div><div class="dims"><div class="dim-row"><span>외형 폭 <span>W</span></span><strong>2,500 mm</strong></div><div class="dim-row"><span>외형 높이 <span>H</span></span><strong>1,620 mm</strong></div><div class="dim-row"><span>벽체 두께 <span>T</span></span><strong>120 mm</strong></div><div class="divider"></div><div class="dim-row"><span>모델 깊이 <span class="guess">L · 시각화값</span></span><strong id="depthReadout">1,600 mm</strong></div></div><div class="range-wrap"><div class="range-top"><label for="depth">깊이 조절</label><output id="depthOutput">1,600 mm</output></div><input id="depth" type="range" min="800" max="3200" step="100" value="1600"></div></section>
|
||||
<section class="card"><div class="card-head"><span class="card-title">표현 요소</span><span class="card-kicker">LEGEND</span></div><div class="materials"><div class="mat-row"><i class="swatch"></i>철근 콘크리트<small>회색</small></div><div class="mat-row"><i class="swatch rebar"></i>철근 배근 개략<small>표시 전환 가능</small></div><div class="mat-row"><i class="swatch dimline"></i>치수 기준 형상<small>mm</small></div></div></section>
|
||||
<section class="card info"><div class="card-head"><span class="card-title"><i class="icon">i</i>모델 참고</span></div><p>도면의 <strong>2,500 × 1,620 mm</strong> 외형 치수와 <strong>120 mm</strong> 치수 표기를 반영한 개략 모델입니다. 도면에서 깊이를 특정하기 어려워 조절값으로 제공하며, 철근 표시는 배근 형상을 이해하기 위한 시각화입니다.</p><div class="file-name">R2C_일반도_20260601.pdf</div></section>
|
||||
<p class="footer-note">형상 검토용 시각화 · 제작·시공 전 원본 도면과 구조 검토 자료를 확인하세요.</p>
|
||||
</aside>
|
||||
</main>
|
||||
</div>
|
||||
<script>
|
||||
(()=>{
|
||||
const canvas=document.getElementById('scene'),ctx=canvas.getContext('2d');
|
||||
const W=2500,H=1620,T=120;
|
||||
let L=1600, yaw=-.72,pitch=.48,zoom=1,showRebar=true,drag=null;
|
||||
const fmt=n=>new Intl.NumberFormat('ko-KR').format(n);
|
||||
function resize(){const r=canvas.getBoundingClientRect(),d=Math.min(devicePixelRatio||1,2);canvas.width=Math.round(r.width*d);canvas.height=Math.round(r.height*d);ctx.setTransform(d,0,0,d,0,0);draw()}
|
||||
const V=(x,y,z)=>[x,y,z];
|
||||
function quad(a,b,c,d,color,edge='#67737b',normal){return {pts:[a,b,c,d],color,edge,normal}}
|
||||
function faces(){
|
||||
const x=W/2,y=L/2,z=H,ix=x-T,iy=y,iz0=T,iz1=H-T, f=[];
|
||||
const cTop='#c4cdd0',cFront='#d2d9da',cSide='#aeb9be',cInner='#99a7ad',cBottom='#aab5b9';
|
||||
// outside shell surfaces
|
||||
f.push(quad(V(-x,-y,z),V(x,-y,z),V(x,y,z),V(-x,y,z),cTop,'#76838a',[0,0,1]));
|
||||
f.push(quad(V(-x,-y,0),V(-x,y,0),V(x,y,0),V(x,-y,0),cBottom,'#68767d',[0,0,-1]));
|
||||
f.push(quad(V(-x,-y,0),V(-x,-y,z),V(-x,y,z),V(-x,y,0),cSide,'#66747c',[-1,0,0]));
|
||||
f.push(quad(V(x,-y,0),V(x,y,0),V(x,y,z),V(x,-y,z),'#b8c2c6','#6d7980',[1,0,0]));
|
||||
// tunnel interior
|
||||
f.push(quad(V(-ix,-y,iz1),V(ix,-y,iz1),V(ix,y,iz1),V(-ix,y,iz1),'#b8c2c5','#6c7a81',[0,0,-1]));
|
||||
f.push(quad(V(-ix,-y,iz0),V(-ix,y,iz0),V(ix,y,iz0),V(ix,-y,iz0),'#a9b5b9','#68777d',[0,0,1]));
|
||||
f.push(quad(V(-ix,-y,iz0),V(-ix,-y,iz1),V(-ix,y,iz1),V(-ix,y,iz0),cInner,'#64727a',[1,0,0]));
|
||||
f.push(quad(V(ix,-y,iz0),V(ix,y,iz0),V(ix,y,iz1),V(ix,-y,iz1),'#9eacb1','#64727a',[-1,0,0]));
|
||||
// visible entrance ring at front
|
||||
f.push(quad(V(-x,-y,z),V(x,-y,z),V(ix,-y,iz1),V(-ix,-y,iz1),cFront,'#68767d',[0,-1,0]));
|
||||
f.push(quad(V(-x,-y,0),V(-ix,-y,iz0),V(ix,-y,iz0),V(x,-y,0),'#bdc7ca','#68767d',[0,-1,0]));
|
||||
f.push(quad(V(-x,-y,0),V(-x,-y,z),V(-ix,-y,iz1),V(-ix,-y,iz0),'#c8d1d3','#68767d',[0,-1,0]));
|
||||
f.push(quad(V(x,-y,0),V(ix,-y,iz0),V(ix,-y,iz1),V(x,-y,z),'#c2cccf','#68767d',[0,-1,0]));
|
||||
// rear ring, visible as a shaded face through tunnel
|
||||
f.push(quad(V(-x,y,z),V(-ix,y,iz1),V(ix,y,iz1),V(x,y,z),'#b6c0c3','#77838a',[0,1,0]));
|
||||
f.push(quad(V(-x,y,0),V(x,y,0),V(ix,y,iz0),V(-ix,y,iz0),'#9eaaaf','#77838a',[0,1,0]));
|
||||
f.push(quad(V(-x,y,0),V(-ix,y,iz0),V(-ix,y,iz1),V(-x,y,z),'#a9b5b9','#77838a',[0,1,0]));
|
||||
f.push(quad(V(x,y,0),V(x,y,z),V(ix,y,iz1),V(ix,y,iz0),'#a5b1b6','#77838a',[0,1,0]));
|
||||
return f;
|
||||
}
|
||||
function rot(p){let [x,y,z]=p;const cy=Math.cos(yaw),sy=Math.sin(yaw),cp=Math.cos(pitch),sp=Math.sin(pitch);const x1=x*cy-y*sy,y1=x*sy+y*cy;return [x1,y1*cp-z*sp,y1*sp+z*cp]}
|
||||
function projection(){const r=canvas.getBoundingClientRect(),scale=Math.min(r.width/(W+L*.68),r.height/(H+L*.5))*1.14*zoom;return {cx:r.width*.5,cy:r.height*.53,scale}}
|
||||
function project(p,P){const [x,y,z]=rot(p);return [P.cx+x*P.scale,P.cy-(z-y*.45)*P.scale]}
|
||||
function shade(hex,n){const l=Math.max(.72,Math.min(1.12,.88+(-n[0]*.13-n[1]*.17+n[2]*.22)));const h=hex.replace('#',''),v=[0,2,4].map(i=>Math.min(255,Math.round(parseInt(h.slice(i,i+2),16)*l)));return `rgb(${v.join(',')})`}
|
||||
function drawGrid(P){const r=canvas.getBoundingClientRect();ctx.fillStyle='#e7edf0';ctx.fillRect(0,0,r.width,r.height);ctx.save();ctx.strokeStyle='#d5dfe3';ctx.lineWidth=1;const extent=4200,step=400;for(let x=-extent;x<=extent;x+=step){const a=project([x,-extent,-4],P),b=project([x,extent,-4],P);ctx.beginPath();ctx.moveTo(...a);ctx.lineTo(...b);ctx.stroke()}for(let y=-extent;y<=extent;y+=step){const a=project([-extent,y,-4],P),b=project([extent,y,-4],P);ctx.beginPath();ctx.moveTo(...a);ctx.lineTo(...b);ctx.stroke()}ctx.restore()}
|
||||
function line3(a,b,P,color,width=1){const A=project(a,P),B=project(b,P);ctx.beginPath();ctx.moveTo(...A);ctx.lineTo(...B);ctx.strokeStyle=color;ctx.lineWidth=width;ctx.stroke()}
|
||||
function drawRebar(P){const rebar='#d97567',thin=2;
|
||||
// perimeter bars following the two opening rings
|
||||
for(const y of [-L/2+T*.46,L/2-T*.46]){
|
||||
const x=W/2-T*.34,z0=T*.48,z1=H-T*.48;
|
||||
const corners=[V(-x,y,z0),V(x,y,z0),V(x,y,z1),V(-x,y,z1)];
|
||||
for(let i=0;i<4;i++)line3(corners[i],corners[(i+1)%4],P,rebar,thin);
|
||||
}
|
||||
// longitudinal bars at slab edges and sidewalls
|
||||
for(const xx of [-W/2+T*.46,W/2-T*.46])for(const zz of [T*.47,H-T*.47])line3(V(xx,-L/2+T*.46,zz),V(xx,L/2-T*.46,zz),P,rebar,thin);
|
||||
for(let i=1;i<=5;i++){const x=-W/2+(W*i/6);line3(V(x,-L/2+T*.46,T*.48),V(x,-L/2+T*.46,H-T*.48),P,'#cb7165',1.25)}
|
||||
// simple ties along the visible near side of the two walls
|
||||
for(let i=1;i<8;i++){const yy=-L/2+T+(L-2*T)*i/8;line3(V(-W/2+T*.42,yy,T*.42),V(-W/2+T*.42,yy,H-T*.42),'#d8897d',1);line3(V(W/2-T*.42,yy,T*.42),V(W/2-T*.42,yy,H-T*.42),'#d8897d',1)}
|
||||
}
|
||||
function dimension(a,b,label,offset,P){const A=project(a,P),B=project(b,P),ox=offset[0],oy=offset[1],C=[A[0]+ox,A[1]+oy],D=[B[0]+ox,B[1]+oy];ctx.save();ctx.strokeStyle='#b17935';ctx.fillStyle='#795421';ctx.lineWidth=1;ctx.setLineDash([4,3]);ctx.beginPath();ctx.moveTo(...A);ctx.lineTo(...C);ctx.moveTo(...B);ctx.lineTo(...D);ctx.setLineDash([]);ctx.moveTo(...C);ctx.lineTo(...D);ctx.stroke();const ang=Math.atan2(D[1]-C[1],D[0]-C[0]);for(const [p,sgn] of [[C,1],[D,-1]]){ctx.beginPath();ctx.moveTo(...p);ctx.lineTo(p[0]+Math.cos(ang+sgn*.45)*8,p[1]+Math.sin(ang+sgn*.45)*8);ctx.moveTo(...p);ctx.lineTo(p[0]+Math.cos(ang-sgn*.45)*8,p[1]+Math.sin(ang-sgn*.45)*8);ctx.stroke()}ctx.font='600 11px system-ui';ctx.textAlign='center';ctx.textBaseline='middle';const mx=(C[0]+D[0])/2,my=(C[1]+D[1])/2;ctx.fillStyle='#e7edf0';ctx.fillRect(mx-35,my-9,70,18);ctx.fillStyle='#795421';ctx.fillText(label,mx,my);ctx.restore()}
|
||||
function draw(){const r=canvas.getBoundingClientRect();if(!r.width||!r.height)return;const P=projection();drawGrid(P);const fs=faces().map(face=>({face,depth:face.pts.reduce((s,p)=>s+rot(p)[1],0)/4})).sort((a,b)=>a.depth-b.depth);ctx.lineJoin='round';for(const {face}=fs){const pts=face.pts.map(p=>project(p,P));ctx.beginPath();ctx.moveTo(...pts[0]);for(let i=1;i<pts.length;i++)ctx.lineTo(...pts[i]);ctx.closePath();ctx.fillStyle=shade(face.color,face.normal);ctx.fill();ctx.strokeStyle=face.edge;ctx.lineWidth=1.1;ctx.stroke()}if(showRebar)drawRebar(P);dimension(V(-W/2,-L/2,0),V(W/2,-L/2,0),'2,500 mm',[0,36],P);dimension(V(-W/2,-L/2,0),V(-W/2,-L/2,H),'1,620 mm',[-48,-2],P)}
|
||||
const views={iso:[-.72,.48],front:[0,.02],side:[-Math.PI/2,.02],top:[0,Math.PI/2-.02]};
|
||||
document.querySelectorAll('.view-btn').forEach(b=>b.addEventListener('click',()=>{document.querySelectorAll('.view-btn').forEach(x=>x.classList.toggle('active',x===b));[yaw,pitch]=views[b.dataset.view];draw()}));
|
||||
document.getElementById('depth').addEventListener('input',e=>{L=Number(e.target.value);const val=fmt(L)+' mm';document.getElementById('depthOutput').textContent=val;document.getElementById('depthReadout').textContent=val;draw()});
|
||||
document.getElementById('rebarBtn').addEventListener('click',e=>{showRebar=!showRebar;e.currentTarget.classList.toggle('active',showRebar);e.currentTarget.setAttribute('aria-pressed',String(showRebar));draw()});
|
||||
document.getElementById('resetBtn').addEventListener('click',()=>{[yaw,pitch]=views.iso;zoom=1;L=1600;document.getElementById('depth').value=L;document.getElementById('depthOutput').textContent='1,600 mm';document.getElementById('depthReadout').textContent='1,600 mm';draw();const t=document.getElementById('toast');t.textContent='기본 시점으로 돌아왔습니다';t.classList.add('show');setTimeout(()=>t.classList.remove('show'),1300)});
|
||||
canvas.addEventListener('pointerdown',e=>{drag={x:e.clientX,y:e.clientY,yaw,pitch};canvas.setPointerCapture(e.pointerId)});canvas.addEventListener('pointermove',e=>{if(!drag)return;yaw=drag.yaw+(e.clientX-drag.x)*.008;pitch=Math.max(-.08,Math.min(1.48,drag.pitch+(e.clientY-drag.y)*.006));document.querySelectorAll('.view-btn').forEach(x=>x.classList.remove('active'));draw()});canvas.addEventListener('pointerup',()=>drag=null);canvas.addEventListener('pointercancel',()=>drag=null);canvas.addEventListener('wheel',e=>{e.preventDefault();zoom=Math.max(.55,Math.min(1.8,zoom*Math.exp(-e.deltaY*.001)));draw()},{passive:false});
|
||||
new ResizeObserver(resize).observe(canvas);resize();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user