- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환 - Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용 - pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관) - worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md) - D:\Rhino-MCP 경로 참조를 새 위치로 치환 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
79 lines
8.7 KiB
JavaScript
79 lines
8.7 KiB
JavaScript
|
|
/* ---------- S02 new pier fit (sample: Ungok P1 drawings uploaded) ---------- */
|
|
var QS = [['foundation', '기초 형식(말뚝 유무)'], ['soffit', '코핑 하면 형상'], ['bearings', '받침 개수']];
|
|
var OPTS = { foundation: ['기초판+말뚝', '기초판만', '말뚝만'], soffit: ['곡선', '직선 경사', '나팔형'], bearings: ['2', '4', '10'] };
|
|
var TPLS = [
|
|
{ n: '운곡교P1형', p: { foundation: '기초판+말뚝', soffit: '곡선', bearings: '10' } },
|
|
{ n: '옥성교P1형', p: { foundation: '기초판+말뚝', soffit: '직선 경사', bearings: '10' } },
|
|
{ n: '새재1교P1형', p: { foundation: '기초판만', soffit: '직선 경사', bearings: '10' } },
|
|
{ n: 'Ramp-A교P1형', p: { foundation: '말뚝만', soffit: '나팔형', bearings: '2' } },
|
|
{ n: '산외교울산P1형', p: { foundation: '기초판만', soffit: '직선 경사', bearings: '4' } }
|
|
];
|
|
var EST = {
|
|
foundation: ['기초판+말뚝', "'말뚝' 문자 7개: D1 CS-TEXT (복합말뚝)", '보통'],
|
|
soffit: ['곡선', "R 12000 mm: D1 CS-DIMT (8257, 16513) 'R=12.000'", '보통'],
|
|
bearings: ['10', 'ELEVATION TABLE 행 10개: D2 (26179, 9342)', '보통']
|
|
};
|
|
var N = { up: false, ans: null, tpl: '운곡교P1형', name: '', rs: '미확인', rm: '미확인', skew: '', brot: '', frot: '', err: '' };
|
|
function nMatch(t) { return QS.filter(function (q) { return t.p[q[0]] === EST[q[0]][0]; }).length; }
|
|
function nBad() {
|
|
var t = TPLS.filter(function (x) { return x.n === N.tpl; })[0], bad = [];
|
|
QS.forEach(function (q) { if (t.p[q[0]] !== N.ans[q[0]]) bad.push(q[1]); });
|
|
if (N.rs === '아니오') bad.push('철근 형상 종류');
|
|
if (N.rm === '아니오') bad.push('철근 생성 방식');
|
|
return bad;
|
|
}
|
|
function viewNew() {
|
|
setWhere('S02 · 새 교각 적합성 판별');
|
|
var html = '<div class="head"><span class="eyebrow">새 교각</span><h1>도면으로 템플릿 추천</h1><p class="lead">도면을 먼저 올리면 앱이 가장 비슷한 템플릿을 추천한다. 추천은 제안이고, 6문항은 사람이 확인한다.</p></div>';
|
|
if (!N.up) {
|
|
html += '<section class="card" style="max-width:38em"><div class="field"><label for="dw">교각 도면(DWG, 여러 장)</label><input id="dw" type="file" accept=".dwg" multiple><span class="help">시안에서는 운곡교 P1 도면 3장을 올린 것으로 본다</span></div><div class="row"><button class="btn primary" type="button" id="up">도면 읽고 추천 받기</button></div></section>';
|
|
main.innerHTML = html;
|
|
$('#up').onclick = function () { N.up = true; N.ans = {}; QS.forEach(function (q) { N.ans[q[0]] = EST[q[0]][0]; }); viewNew(); };
|
|
return;
|
|
}
|
|
var ranked = TPLS.slice().sort(function (a, b) { return nMatch(b) - nMatch(a); });
|
|
html += '<section class="strip"><p class="concl">도면 3장, 문자 2,366개를 읽었다 — 1위 ' + ranked[0].n + ' (3 / 3 일치)</p></section>';
|
|
html += '<h2>1 추천 순위</h2><div class="tw"><table class="t"><caption>추정한 3문항과 일치한 수</caption><thead><tr><th scope="col">순위</th><th scope="col">템플릿</th><th class="num" scope="col">일치</th>' + QS.map(function (q) { return '<th scope="col">' + q[1] + '</th>'; }).join('') + '</tr></thead><tbody>';
|
|
ranked.forEach(function (t, i) {
|
|
html += '<tr><td class="num">' + (i + 1) + '</td><td><b>' + t.n + '</b></td><td class="num">' + nMatch(t) + ' / 3</td>' + QS.map(function (q) {
|
|
var ok = t.p[q[0]] === EST[q[0]][0];
|
|
return '<td>' + esc(t.p[q[0]]) + ' ' + (ok ? badge('good', '●', '일치') : badge('bad', '■', '다름')) + '</td>';
|
|
}).join('') + '</tr>';
|
|
});
|
|
html += '</tbody></table></div>';
|
|
html += '<h2>2 추정 근거</h2><div class="tw"><table class="t"><thead><tr><th scope="col">문항</th><th scope="col">추정</th><th scope="col">근거(도면 위치)</th><th scope="col">신뢰</th></tr></thead><tbody>';
|
|
QS.forEach(function (q) { var e = EST[q[0]]; html += '<tr><td>' + q[1] + '</td><td><b>' + e[0] + '</b></td><td><span class="chip">' + esc(e[1]) + '</span></td><td>' + e[2] + '</td></tr>'; });
|
|
html += '<tr><td>기둥 단면·철근 형상·철근 생성 방식</td><td>' + badge('dash', '◇', '판단 불가') + '</td><td>도면 문자로 판별할 근거가 없다. 사람이 답한다</td><td>-</td></tr></tbody></table></div>';
|
|
html += '<div class="banner tip"><span class="m" aria-hidden="true">◆</span><div>코핑 하면 형상은 정면도의 큰 R 문자(10 m 이상)만 본다. 평면 모서리의 작은 R은 제외한다. 평면의 사각·직각과 각도는 문항이 아니라 아래 값으로 입력한다.</div></div>';
|
|
html += '<h2>3 6문항 확인 후 교각 만들기</h2><div style="max-width:38em;display:flex;flex-direction:column;gap:var(--prism-space-200)">';
|
|
QS.forEach(function (q) {
|
|
html += '<div class="field"><label for="a_' + q[0] + '">' + q[1] + '</label><select id="a_' + q[0] + '" data-a="' + q[0] + '">' + OPTS[q[0]].map(function (o) { return '<option' + (N.ans[q[0]] === o ? ' selected' : '') + '>' + o + '</option>'; }).join('') + '</select></div>';
|
|
});
|
|
[['rs', '철근 형상 종류가 템플릿의 재료표 형식 열과 같은가'], ['rm', '철근 생성 방식(규칙 값 / 철근별 중심선)이 같은가']].forEach(function (q) {
|
|
html += '<div class="field"><label for="' + q[0] + '">' + q[1] + '</label><select id="' + q[0] + '" data-r="' + q[0] + '">' + ['미확인', '예', '아니오'].map(function (o) { return '<option' + (N[q[0]] === o ? ' selected' : '') + '>' + o + '</option>'; }).join('') + '</select></div>';
|
|
});
|
|
html += '<fieldset class="card sunken"><legend class="lbl">각도 값 확인 <span class="muted">템플릿을 가르지 않는 값. 비워도 된다</span></legend><div class="grid3">' +
|
|
[['skew', '코핑 평면 skew (°)'], ['brot', '받침 회전 (°)'], ['frot', '기초 회전 (°)']].map(function (q) { return '<div class="field"><label for="g_' + q[0] + '">' + q[1] + '</label><input id="g_' + q[0] + '" type="text" inputmode="decimal" data-g="' + q[0] + '" value="' + esc(N[q[0]]) + '"></div>'; }).join('') + '</div></fieldset>';
|
|
var bad = nBad();
|
|
html += '<section class="strip" role="status" aria-live="polite"><h3 style="margin:0 0 .25rem">결과 판정</h3><p class="concl">' + (bad.length ? badge('bad', '■', '같은 템플릿으로 만들 수 없음') + ' 다른 문항: ' + esc(bad.join(', ')) : badge('good', '●', '이 템플릿으로 만들 수 있음')) + '</p>' +
|
|
(bad.length ? '<p class="muted" style="margin:.25rem 0 0">가장 가까운 템플릿에 생성기·검사기를 추가해야 한다. 그래도 진행하면 교각 이름 옆과 모든 화면 상단에 형식 불일치 경고가 계속 표시된다(닫을 수 없다).</p>' : '') + '</section>';
|
|
html += '<div class="field' + (N.err ? ' bad' : '') + '"><label for="nm">교각 이름<span class="req">필수</span></label><input id="nm" type="text" value="' + esc(N.name) + '" style="max-width:20rem">' + (N.err ? '<span class="err"><span aria-hidden="true">■</span>' + esc(N.err) + '</span>' : '') + '</div>';
|
|
html += '<div class="field"><label for="tp">템플릿</label><select id="tp" style="max-width:20rem">' + ranked.map(function (t, i) { return '<option value="' + t.n + '"' + (N.tpl === t.n ? ' selected' : '') + '>' + (i + 1) + '위 ' + t.n + '</option>'; }).join('') + '</select></div>';
|
|
html += '<div class="row"><button class="btn primary" type="button" id="mk">' + (bad.length ? '그래도 만들기' : '교각 만들기') + '</button></div></div>';
|
|
main.innerHTML = html;
|
|
var y = window.scrollY;
|
|
main.querySelectorAll('[data-a]').forEach(function (e) { e.onchange = function () { N.ans[e.getAttribute('data-a')] = e.value; viewNew(); window.scrollTo(0, y); $('#' + e.id).focus(); }; });
|
|
main.querySelectorAll('[data-r]').forEach(function (e) { e.onchange = function () { N[e.getAttribute('data-r')] = e.value; viewNew(); window.scrollTo(0, y); $('#' + e.id).focus(); }; });
|
|
main.querySelectorAll('[data-g]').forEach(function (e) { e.oninput = function () { N[e.getAttribute('data-g')] = e.value; }; });
|
|
$('#nm').oninput = function () { N.name = this.value; };
|
|
$('#tp').onchange = function () { N.tpl = this.value; viewNew(); window.scrollTo(0, y); $('#tp').focus(); };
|
|
$('#mk').onclick = function () {
|
|
N.name = $('#nm').value.trim();
|
|
if (!N.name) { N.err = '교각 이름이 비어 있다. 폴더를 만들 이름이 필요하다. 이름을 입력한다'; viewNew(); $('#nm').focus(); return; }
|
|
N.err = '';
|
|
toast("교각 '" + N.name + "'을(를) 만들었다. 올린 도면이 교각 폴더로 옮겨졌고 6문항 답과 각도 값이 저장되었다." + (bad.length ? ' 형식 불일치 경고가 표시된다.' : ''));
|
|
viewNew();
|
|
};
|
|
}
|