Doop
Doop: 오픈소스 멀티플레이어 디자인 캔버스 및 AI 에이전트용 MCP
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기브라우저에서 G-code·Heidenhain·woodWOP MPR 파일을 열고 3D 공구 경로를 확인하는 무료 NC 뷰어 (OpenCAD)
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗NC 프로그램(G-code·Heidenhain 평문·woodWOP MPR·WangShi PGI·KDT XML)을 브라우저 안에서 읽어 3D 공구 경로로 보여 주는 뷰어 앱과, 그 해석기 라이브러리입니다.
해석기는 DOM·WebGL에 기대지 않아 웹 워커에서 돌고, 결과 장면(Scene3d)은 opencad-viewer3d로 그립니다.
다른 NC 해석 라이브러리를 쓰지 않고 새로 작성했습니다. G-code·Heidenhain 평문·woodWOP MPR은 공개 자료를 보고 작성했습니다.
WangShi PGI·KDT XML은 공개 형식 설명서가 없어, 비공개 리더의 형식 설명과 실제 출력 파일을 보고 맞췄습니다. 그 리더의 코드는 옮기지 않았습니다.
src/siemens.ts — %N…_MPF 머리·MCALL·CYCLE·T="…"·CR=·AC/IC·GOTOF·DEF·SUPA 등으로 판별):
; 주석, 주소 = 식(X=R1*2), 축별 AC(…)/IC(…), CR=·I=AC(…)·AR=·CIP(I1/J1/K1)·TURN=, 극좌표 G110FLAG_START_EST, 블록 상태에 "출발 높이 추정"). 그 높이에서 출발한 급속은 점선 묶음이지만 끝점은 실제라 범위와 급속 깊이 확인에 넣는다
(G28 뒤 G43 H2 Z-10.처럼 가장 깊은 절삭보다 아래로 급속하면 경고). 고정 사이클이 추정 높이에서 시작하면 처음 높이(G98)로 돌아가는 급속도 같은 추정 높이로 그린다.
라이브러리 옵션 machineOffset(첫 작업 좌표계 원점의 기계 좌표)·referencePoint(또는 machine.offsetMm·referenceMm, 앱 "기계·읽기 설정")를 주면 실제 위치로 그린다machine, mm 기준 — inch 프로그램은 바꿔 씀): 급속 속도 rapidFeedMm, 공구 교환 한 번의 시간 toolChangeSec(예상 시간에 더함),
도그레그 급속 dogleg(여러 축 급속을 축마다 같은 속도로 함께 가다 짧은 축부터 멈추는 꺾인 길로 그리고 가장 긴 축 이동으로 시간 계산),
축별 허용 범위 limitsMm(작업 좌표 기준, 벗어나는 이동을 줄과 함께 경고). G-code blockDelete·checkToolNumbers와 함께 앱 설정에서 고른다T1 D=10.) 차례로 찾음. 뷰어 앱은 보정 조각(직선·호 — 호 중심·중심각)을 남겨,
측정이 공구 중심 경로의 호 위에서 가운데 점·중심·접선·곡선 위 점을 구합니다.
LinuxCNC 방언은 설명서(G-code 절 "G40"·"G41, G42"와 "Cutter Compensation"의 Programming Entry Moves)대로, 보정을 켠 뒤 평면 급속이
이어지면 마지막 급속만 보정 자리로 가므로 앞 급속은 보정 없이 그리고 마지막 급속을 들어가기로 봅니다. 들어가기 이동이 공구 반지름보다 짧거나,
G40 뒤 첫 이동이 원호이거나 공구 지름보다 짧은 직선이면(LinuxCNC는 오류로 멈춤) 보정 경로를 그릴 때 경고합니다
(길이는 보정 평면 안 — 밀링·선반 모두. 다른 방언은 보정을 켠 뒤 첫 평면 이동을 들어가기로 봅니다)toolTable의 radius·orientation), LinuxCNC G10 L1 P R Q 차례로 찾고,
파일에 없으면 앱의 선반 공구 표(옵션 machine.latheToolsMm — 공구 번호마다 mm)에서 찾습니다. Fanuc 선반처럼 D가 없으면 지금 공구 번호(T0101의 01)로 찾습니다.
방향을 모르거나 0이면(설명서가 정하지 않음) 노즈 중심 경로로 그리고 경고하고, LinuxCNC가 아닌 방언에서 방향을 쓰면 번호를 LinuxCNC 그림대로 읽었다고 경고합니다.
밀링 보정 값은 바뀌지 않습니다lathe: 'auto') 선반처럼 보이면 선반 모드로 읽고
3D 보기 위 띠로 알림("밀링으로 읽기" 단추). 라이브러리 기본(옵션 없음)은 밀링으로 읽고 안내 경고latheSystem, 정보 탭 "선반 G 코드"): 기본은 A(위와 같음)이고,
B는 G77·G78·G79가 바깥지름·나사·끝면 단순 사이클, G90/G91이 절대·증분, G92가 좌표 설정·최고 회전수, G94/G95가 분당·회전당 이송,
G98/G99가 고정 사이클 복귀 높이입니다. C는 B에 더해 G20·G21·G24가 단순 사이클, G70/G71이 inch/mm, G72integerUnits, 정보 탭 "해석 설정"): Fanuc 매개변수 DPI = 0 꼴로 X·Y·Z·U·V·W·I·J·K·R·Q는
mm 0.001·inch 0.0001, A·B·C는 0.001도. 반복 횟수(고정 사이클·M98의 K, L)는 그대로이고, 식·변수가 있는 줄도 소수점 없는 수만 바꾸며
변수·식 값은 그대로 씁니다. 선반 G71~G73 윤곽 블록에도 씁니다. 끈 채 소수점 없는 100 이상 좌표가 절반을 넘으면 경고와 띠("최소 단위로 읽기")윤곽:요소)을 가리키고, 실제 파일에서는 모두 판 외곽 윤곽의 한 변이었습니다.
DK는 띠 두께로 봅니다. 같은 가공의 설명(KM·KAT)에 적힌 띠 두께와 늘 같고, 파일 머리 주석에 변마다 적힌 밴딩 두께와 1,965개 모두 맞았습니다
(두께가 0인 변에는 Easyedge가 없음).
그래서 이 구간의 판 옆면에 두께 DK의 띠를 붙인다고 보고, 묶음 "엣지 밴딩"(청록 선)으로 그립니다. 판 재료에서 먼 쪽으로 DK만큼 띄운 윗변·아랫변과
양 끝 세로선입니다. 띠 쪽은 닫힌 윤곽이 판 가장자리에 닿으면 바깥, 판 안에 떠 있으면(따낸 구멍) 안쪽, 열린 윤곽이면 판 가운데에서 먼 쪽입니다.
공구 경로가 아니라서 윤곽 가공 수·시간·이송 길이에는 넣지 않고, 가공 목록에 "엣지 밴딩 · 두께 1"처럼 보입니다.
MDA·MDE(TAN·TAN_AB — 붙이기 시작·끝의 접근 꼴로 보임)·RI·NM은 모든 파일에서 값이 하나뿐이라 뜻을 가릴 수 없어 쓰지 않습니다.윤곽:요소)가 가리키는 윤곽은 모두 닫혀 있었고
(Ø35 원 171개, 직사각형 17개, 직선과 호가 섞인 긴 구멍 10개), 그 안을 깊이 |TI|만큼 판 기둥(묶음 "포켓")으로 그립니다. 원이면 목록에 지름도 보입니다.
실제 파일의 TI는 모두 음수(-2~-18)였고, 이 포켓은 늘 아랫면 드릴(UfluBohr)과 같은 면 묶음에 있었습니다(파일 주석의 면 표시가 같고, Ø35 컵 옆의
Ø3 나사 구멍이 UfluBohr). 이름에 "from below"가 붙은 것도 있습니다. 그래서 TI가 음수면 아랫면(Z 0)에서 위로, 0보다 크면 다른 포켓처럼 윗면에서 아래로
봅니다. 판 두께보다 깊으면 두께까지만 그립니다. AD·AZ·ZU·ZT·XY·HP·SP·ASG·WW·MX 같은 나머지 값은 공구 경로를 정하는 값으로 보이지만,
공구 경로는 그리지 않으므로 쓰지 않습니다.<pgi>)·KDT XML(.xml, 뿌리 <KDTPanelFormat>): 판재 가공 센터(KDT 등) 프로그램. MPR과 같은 판재 가공 모델로 읽습니다.
<params>·<Params>, 값에 W-37·300+(40)/2 같은 식), 직사각형이 아닌 판의 외곽선(bulge 호 포함)direction z-·z+·x+·x-·y+·y-, XML TypeNo 1·8·2 + Quadrant), 구멍 줄(no·xi·yi·zi), 노치 벽 위의 옆면 구멍,
공구가 원을 다 채우는 35 mm 도어 컵(T35)line isSaw, XML 3·13 UseSaw)과 직선 경로, 사각 포켓(가운데·최소 모서리, 둥근 모서리·회전, 옆면 포함), 원형 포켓·원 경로,
라우팅 경로(PGI bulge — ±1은 들어오는 접선을 잇는 호, XML <Arc Radius Direction>, 상대 좌표), 닫힌 empty 경로는 포켓으로z·centerZ, XML Z1)는 아랫면에서 잽니다. 윗면·아랫면 위치는 둘 다 위에서 본 (x, y)입니다.enable·Enable 0), 모르는 요소는 위치만 점으로(XML FlatAnglePull·Engraving은 한국어 이름), 깨진 값은 '해석 실패'로 목록에.
문서 순서와 다른 order, 그리지 않는 진입 값(entryL·entryZ·toolExtraOffset), 면에서 벗어난 옆면 구멍은 경고로 알립니다..xml이라도 <KDTPanelFormat>이 아니면 G-code로 읽지 않고 알리며, woodWOP MPRX(XML)는 MPR로 저장해 열라고 안내합니다.src/xml.ts — 요소마다 원본 줄, DOCTYPE·개체 선언은 풀지 않음, 끊긴 문서는 닫힌 요소까지).combineNcJob·combineNcFiles·splitNcJob, src/job.ts): 주 프로그램 뒤에 나머지 파일을
표지 줄(G-code (OPENCAD-JOB-FILE 이름), Siemens·Heidenhain ; OPENCAD-JOB-FILE 이름)로 나눠 잇고, 해석기는 표지 줄에서
주 프로그램을 끝내며 부르기를 이은 글 전체에서 찾는다 — Fanuc·Haas M98 P·G65 P(O 줄, 없으면 파일 이름 O1001.nc·1001.nc),
LinuxCNC o<이름> call, Siemens L123·이름·이름(인수)·CALL(파일 이름, 파일 머리 %N…_SPF·PROC), Heidenhain CALL PGM·SEL PGM +
CALL SELECTED PGM·DIN/ISO %이름(BEGIN PGM·%이름 머리, LBL은 프로그램마다 따로). M99·M17·RET·END PGM 없이 파일 끝에 닿은
부프로그램은 부른 곳으로 돌아간다. 주 프로그램은 고르지 않으면 다른 파일이 부르지 않는 파일 가운데 부르기가 가장 많고
M30·M2로 끝나는 쪽(Siemens는 .MPF). 형식이 다른 파일은 잇지 않음. 찾지 못한 프로그램은 경고와 함께 notices의
missing-program(이름 목록)으로 알리고, O9000번대 G65는 기계에 든 매크로로 보아 건너뜀checkToolNumbers: false로 끔),
공구 교환 뒤 G43 없이 절삭(프로그램이 G43을 쓸 때 — 밀링 Fanuc 계열), 주축이 멈춘 채(M5·S0) 절삭(주축을 쓰는 프로그램에서, GRBL 빼고),
급속이 가장 깊은 절삭보다 아래로, 공작물(Heidenhain BLK FORM) 윗면보다 낮은 급속 수평 이동setMesh — 공구 경로는 다시 올리지 않고, 찍은 측정 점·고른 것·단면도 그대로라 측정 중에도 소재가 바로 바뀝니다).TOUCH_DRAG_START_PX)보다 덜 움직이고 떼면 누르기로 보고(6~9화소 떨려도 모형이 돌지 않음),
누르기·측정 점은 선분에서 14화소(코어 touchPickTolerance 기본값) 안이면 고르며 끝점에서 14화소 안이면 끝점에 붙잡습니다(마우스는 6·10화소 그대로).
보기 도구 막대에 전체 보기, 표준 뷰, 원근/평행, 그리기 모드(음영·음영+모서리·와이어프레임·X-ray), 격자, 뷰 큐브, 좌표축,
단면(끔·X·Y·Z 축 고르기, 위치 슬라이더, 뒤집기 — 새 문서를 열면 꺼짐), 두 점 측정, PNG 저장, 패널 열고 닫기가 있습니다.
G-code·Heidenhain의 두 점 측정은 끝점 말고도 G2/G3 원호의 중심(구멍 가운데처럼 빈 자리라도 커서가 오면)과 이동의 가운데 점에 붙습니다.
원호의 가운데 점은 현의 가운데가 아니라 호 위의 점이고(나선은 높이도 반), 중심은 해석기가 읽은 값 그대로입니다.
반경 보정 경로(공구 중심)에서는 보정한 조각의 가운데 점과 호 중심에 붙습니다.
판재 파일(MPR·PGI·XML)은 윤곽(자유 형상 포켓 윤곽 포함)과 판 외곽선의 원호 중심, 지름을 아는 원형 포켓의 중심, 고른 윤곽 요소(직선·호)의 가운데 점에 붙습니다.
측정 중에는 판·포켓·홈·구멍의 모서리선(이미 그려 둔 선)도 고르므로 판 외곽선, 포켓 둘레(둥근 사각 포켓의 모서리 — MPR Tasche의 RD도, 경로 포켓의 호, 원형 포켓 —
옆면에서 판 것도), 구멍 테두리(위·아래 원 — 옆면 구멍도)의 호에서도 호의 가운데 점·중심에 붙습니다.
스플라인(G5·G5.1·G5.2/G5.3)의 가운데 점은 나눈 조각 하나의 가운데가 아니라 곡선 전체의 길이 가운데(곡선 위)입니다.
원호(나선, 반경 보정 경로의 호, 판재 윤곽·판 외곽선·포켓 둘레·구멍 테두리의 호 포함)와 스플라인은 짧은 직선 토막으로 그리므로 그 토막 위를 찍거나
토막에 수직 발을 내리면 원래는 곡선에서 조금 안쪽에 찍혔습니다. 이제 찍힌 점을 정확한 곡선 위로 옮겨(3D 뷰어 코어 0.14.0 다듬기)
거리·dx·dy·dz가 곡선 위 값입니다 — 예를 들어 원호 중심에서 원호 위 아무 점까지 잰 거리가 반지름 그대로이고, 원 밖 점에서 내린 수직 발은
반지름 방향의 원 위 점입니다. 첫 점이 원호에서 멀어 토막마다 내린 발이 모두 토막 밖으로 떨어져도 곡선 위 진짜 발에 붙습니다.
두 경로의 교차점도 원호·스플라인이 끼면 토막끼리 만나는 자리가 아니라 두 곡선이 실제로 만나는 점입니다(3D 뷰어 코어 0.15.0 다듬기 —
직선과 원, 두 원은 식으로, 스플라인·나선이 끼면 두 매개변수 뉴턴으로 풂). 다른 평면의 두 원호처럼 토막끼리는 조금 비켜 지나가도
곡선이 만나면 붙고, 높이가 달라 3D로 만나지 않는 두 경로에는 붙지 않습니다.
판재 파일도 윤곽 경로·구멍 테두리·포켓 둘레·판 외곽선의 호가 다른 경로나 판·포켓·홈의 곧은 모서리선과 만나는 점은 원 위 정확한 교차점입니다
(둥근 포켓의 곧은 변뿐 아니라 각진 포켓·홈 상자·직사각형 판의 모서리도).
어느 형식이든 두 경로가 3D에서 만나는 교차점, 첫 점을 찍은 뒤 커서 둘레 경로로 내린 수직 발에도 붙고,
붙을 점이 없으면 첫 점에서 X·Y·Z축 방향이나 커서가 지나간 경로 선분과 나란한 방향의 점을 잡습니다(3D 뷰어 코어의 추론 붙잡기).
마우스를 올리면 표식 모양으로 종류를 보이고(끝점 네모·가운데 점 세모·중심 ⊕·교차점 ×·수직 발 ⊥, 축 방향은 첫 점에서 축 색 점선,
나란한 방향은 자홍 점선과 ∥), 커서 옆 툴팁에 "끝점"·"X축 방향"·"나란한 방향"처럼 이름을 적습니다.
코어가 그리는 거리 글상자는 툴팁 자리(커서 오른쪽 아래)를 비켜 그리고(3D 뷰어 코어 0.13.0), 글상자가 비킬 곳이 없을 때만
툴팁이 커서 왼쪽이나 위로 비켜 섭니다. 오른쪽 위 뷰 큐브와 3D 보기 위에 뜬 띠(측정 띠·해석 판단 띠·재생 띠)도 비켜 그리므로
두 점이 화면 구석이나 아래쪽에 있어도 거리가 가려지지 않습니다. 겹치지 않으면 자리는 그대로입니다.
결과에는 "중심 → 가운데 점", "끝점 → X축 방향"처럼 두 점의 종류를 적습니다.
방향 잠금(SketchUp 추론 잠금): 첫 점을 찍은 뒤 Shift를 누르고 있으면 지금 방향(축·나란함·수직)을 잠가 그 선 위에만 찍고, 다른 점을
가리키면 그 점을 선으로 옮긴 자리(예: Z축을 잠그고 다른 경로 끝을 가리키면 그 높이)를 찍습니다. 첫 점이 경로 끝이고 그 경로를 따라
다른 끝점·가운데 점·교차점을 가리킬 때도 그 경로 방향(축과 나란하면 그 축)으로 잠급니다 — 경로 몸통을 지나지 않고 끝점으로 바로 가도 됩니다
(3D 뷰어 코어 0.12.0). 상태 줄은 커서 아래에 Shift로 잠글 방향이 있을 때만 "Shift로 X축 방향 잠그기"처럼 알리고, 없으면 "화살표로 방향 잠금"입니다.
화살표 → X · ← Y · ↑ Z축, ↓ 지금 방향(없으면 첫 점 뒤 마지막으로 본 방향)으로 잠그고 같은 키로 풉니다(코드 목록·메뉴·탭에 초점이 있으면 그쪽 키).
잠그면 굵은 안내선과 툴팁·상태 줄의 "X축 방향으로 잠금"으로 알리고, 잠근 선 위에 찍은 결과는 "끝점 → X축 방향 · 잠금"입니다.
둘째 점을 찍거나 지우면 풀립니다.
첫 점이 G2/G3 원호(나선 포함)의 시작이나 끝이고 커서가 그 원호 위(가운데 점·다른 끝 등)에 있으면 Shift·↓·잠금 단추는 그 끝에서의
원호 으로 잠급니다. 첫 점을 원호의 가운데 점이나 원호 위 다른 자리(원호를 나눈 현 위의 아무 점, 현의 끝)에 찍었어도
커서가 그 원호 위에 있으면 그 점의 접선으로 잠급니다. 현 위에 찍은 점은 원호에서 조금 안쪽이라, 원호에서 가장 가까운 자리의 접선을
그 점을 지나게 씁니다. 잠근 방향은 첫 점에서 원호를 따라 커서 쪽으로 나가는 쪽입니다(한 바퀴 원은 가까운 쪽).
판재 파일(MPR·PGI·XML)의 윤곽 가공과 가공 없는 윤곽에 든 호, 판 외곽선·포켓 둘레·구멍 테두리의 호도 끝·가운데 점·호 위 다른 자리에서 같은 접선으로 잠급니다.
보정 경로 보기를 켰을 때 반경 보정 경로(공구 중심 — G41/G42 원호를 공구 반지름만큼 옮긴 호, 바깥 모서리를 도는 호)의 호도 같습니다.
스플라인(G5·G5.1·G5.2/G5.3)도 끝·가운데 점·곡선 위 아무 점에서 그 자리의 정확한 접선으로 잠급니다(조각 위에 찍은 점은 곡선 위 가장 가까운 자리).
원호를 나눈 현 하나의 방향이 아니라 실제 접선이고(나선은 높이 기울기도, G18·G19 평면도 맞음),
툴팁·상태 줄·결과에 "접선 방향"이라 적습니다. 커서 아래가 축·나란함·수직 발이면 그쪽이 먼저입니다.
첫 점이 놓인 곡선은 3D 뷰어 코어 0.16.0이 찍은 점에 실어 주는 선분 식별자로 그 이동(보정 경로면 그 이동의 보정 조각)·윤곽에서 바로 찾으므로, 이동이 아주 많은 프로그램에서도
첫 점을 찍을 때 모든 이동을 다시 훑지 않습니다(원호 20만 개에서 첫 점 한 번 약 31 ms → 0.04 ms). 곡선이 바뀌는 꼭짓점과 원호의 가운데 점·중심은
식별자가 한쪽 이동만 가리키거나 없어서 예전처럼 좌표로 찾습니다.
(SketchUp 추론 따라가기 — 3D 뷰어 코어 0.16.0): 끝점·가운데 점·중심·교차점 같은 점 위에 마우스를 0.5초쯤 멈추면 그 점을 기억하고
(최근 셋까지, 작은 십자로 표시), 그 점을 지나는 X·Y·Z축 위에도 붙습니다(툴팁·결과 "끌어온 점에서 X축", 그 점에서 점선).
첫 점을 찍은 뒤에는 첫 점의 축 위에서 기억한 점과 나란한 자리에도 붙는데, 이 점은 첫 점에서 그 축 방향이라 "X축 방향"으로 적고 Shift로 잠글 수 있습니다.
두 점을 기억하면 한 점의 축과 다른 점의 다른 축이 3D로 만나는 점(툴팁·결과 "끌어온 Y축과 X축의 교차" — 첫 점을 찍기 전에도),
끌어온 축이 경로 선분이나 판·포켓·구멍의 모서리선과 만나는 점("끌어온 점에서 X축 · 모서리 위")에도 붙습니다(3D 뷰어 코어 0.17.0).
높이가 달라 3D로 만나지 않는 두 축에는 붙지 않습니다.
(3D 뷰어 코어 0.28.0): 끌어온 점이 있으면, 커서가 앞서 지나간 곧은 선분(직선 이송·급속·드릴, 보정 경로의 직선,
판재의 곧은 윤곽 요소와 판·포켓·홈의 곧은 모서리선)과 나란하게 그 점에서 그은 선, 그 선분에 수직으로 그은 선 위에도 붙습니다
(툴팁·결과 "끌어온 점에서 모서리와 나란히"·"끌어온 점에서 모서리에 수직", 기준 선분은 자홍 점선). 원호·나선·스플라인과 보정 경로의 호,
판재 윤곽 호·판 외곽선·포켓 둘레·구멍 테두리의 호를 나눈 짧은 현은 기준이 되지 않아, 곡선 위를 지나가도 앞서 지나간 곧은 선분이 기준으로 남습니다.
기준이 X·Y·Z축과 나란한 판 모서리면 그 선은 끌어온 축과 같아 "끌어온 점에서 X축"으로 붙습니다. 첫 점을 지나는 선이 아니어서 Shift로 잠그지 않습니다.
(3D 뷰어 코어 0.18.0): 첫 점을 찍은 뒤 첫 점의 X·Y·Z축이 다른 경로나 판·포켓·구멍의 모서리선과 만나는 점("X축 · 모서리 위"),
첫 점의 축이나 끌어온 축이 공작물(Heidenhain BLK FORM)·판·포켓·홈·구멍·깎기 시뮬레이션 소재의 면을 뚫는 점("X축 · 면 위"·"끌어온 점에서 X축 · 면 위")에도 붙습니다.
첫 점에서 그 축 방향의 점이라 Shift로 그 축에 잠글 수 있습니다. 축이 원호(나선, 반경 보정 경로의 호, 판재 윤곽·판 외곽선·포켓 둘레·구멍 테두리의 호)나
스플라인을 나눈 토막과 만나면 토막 위가 아니라 축과 곡선이 실제로 만나는 점에 붙고(끌어온 축도), 찍힌 점은 축에서 벗어나지 않습니다
(예: 경로 끝에서 X축을 따라 원호까지 잰 dy·dz가 0, 원 위 반지름 그대로). 판재의 구멍 벽·원형 포켓 벽·둥근 사각 포켓 모서리·판 외곽선 둥근 모서리처럼
원기둥 벽을 뚫는 점도 벽을 나눈 삼각형 위가 아니라 축과 원기둥이 실제로 만나는 점입니다(3D 뷰어 코어 0.19.0).
측정 점은 반투명으로 그린 판을 지나 그 뒤 구멍·포켓·홈을 고르므로(3D 뷰어 코어 0.23.0, 포켓·홈은 0.24.0), 판을 숨기지 않아도 판 안 수직·수평 구멍과
포켓·홈의 벽을 뚫는 점에 붙습니다. 판재 구멍은 위·아래가 막힌 기둥으로 그리므로, 위에서 보면 그 윗면을, 비스듬히 보면 판 너머로 보이는 바깥 벽을 고릅니다.
포켓·홈은 반투명으로 그리고, 고를 때는 판 면 자리의 뚜껑만 지나고 벽·바닥에서 멈춥니다(3D 뷰어 코어 0.27.0 — 그림·윤곽선은 그대로).
그래서 판 위에서 포켓·홈의 바닥(위에서 보면 바닥 높이)과 뚜껑 너머로 보이는 안쪽 벽, 포켓 바닥에 뚫은 구멍의 벽을 숨기지 않고 잽니다.
구멍 그물은 판 면에서 시작하므로 포켓 바닥 구멍을 위에서 가리키면 포켓 윗면 높이의 구멍 윗면(중심 점도 그 높이)을 고르고, 구멍 테두리는 포켓 바닥 높이에 따로 없습니다.
포켓 위 테두리(윤곽선)는 예전처럼 고르므로 테두리에서 바닥까지 깊이를 잴 수 있습니다.
뒤에 구멍·포켓·홈이 없는 판 위 자리는 예전과 같습니다.
공작물 상자와 깎기 시뮬레이션 소재의 면은 그린 면 그대로입니다(시뮬레이션 소재의 벽은 격자 한 칸 너비까지만 압니다).
찍은 결과는 "끝점 → X축 · 모서리 위"·"끝점 → Z축 · 면 위"처럼 적습니다.
축이 곡선을 나눈 토막과는 만나지 않고 토막과 곡선 사이만 지나는 자리도 곡선 위 점에 붙습니다(3D 뷰어 코어 0.20.0). 같은 윤곽을 여러 깊이로 깎는 경로에서
위 점에 머물러 끌어온 Z축이 아래 원호를 뚫는 점, 나선 위쪽 점의 Z축이 아래 바퀴를 뚫는 점, 스플라인 위를 지나는 Z축이 그렇습니다.
곡선은 짧은 토막으로 그리므로 이런 축은 토막에서 처짐(5°씩 나눈 원호면 반지름의 0.1% 가까이)만큼 떨어져 지나가, 0.19.0까지는 커서에 가까운 축 위 점에만 붙었습니다.
같은 평면에서 원호 꼭대기를 현과 거의 나란히 스치는 축도 원 위 두 교차에 붙습니다(3D 뷰어 코어 0.22.0·0.23.0 — 반지름 25 원호를 확대 12단으로 재면 8단,
0.20.0은 한 단도 붙지 않았습니다. 남은 단은 축과 현의 틈이 반 화소보다 좁거나 붙잡기 거리보다 넓거나, 꼭짓점이 커서 가까이 있어 끝점이 먼저인 확대입니다).
판 외곽선 둥근 모서리처럼 볼록한 원기둥 벽을 축이 아주 얕게 스쳐, 벽을 나눈 삼각형은 비켜 가고 원기둥만 뚫는 자리도 원기둥 위 점입니다(3D 뷰어 코어 0.22.0).
이런 축이 삼각형 하나 위에서 원기둥을 두 번 뚫어도 두 교차 모두에 붙습니다(3D 뷰어 코어 0.24.0 — 0.23.0까지는 그 삼각형 가운데 쪽 교차 하나만).
(3D 뷰어 코어 0.19.0): Shift·화살표·잠금 단추로 방향을 잠근 동안에도 커서 둘레에 점 붙잡기가 없으면,
잠근 선이 경로·모서리선과 만나는 점("잠근 X축 · 모서리 위")이나 소재 면을 뚫는 점("잠근 Z축 · 면 위")에 붙습니다. 축뿐 아니라 나란한 방향·수직 방향·
원호 접선으로 잠근 선도 같고("잠근 접선 방향 · 모서리 위"), 원호·나선·스플라인·원기둥 벽과는 실제로 만나는 점입니다(축이 아닌 잠근 선은 3D 뷰어 코어 0.20.0부터 —
예: 선분 끝에서 Shift로 그 선분 방향에 잠근 선이 원호와 만나는 점, 비스듬히 잠근 선이 판 외곽선 둥근 모서리를 뚫는 점).
첫 점 축이 원호와 만나는 점을 본 뒤 Shift로 그 축에 잠가도 점이 그 자리에 남습니다. 결과는 "끝점 → 잠근 X축 · 모서리 위"처럼 적습니다.
잠근 동안 끌어온 점이 있으면 잠근 선이 그 점의 X·Y·Z축과 만나는 점("잠근 X축 · 끌어온 점에서 Y축")에도 붙고, 축으로 잠갔으면 잠근 축 위에서
끌어온 점과 나란한 자리("잠근 Z축 · 끌어온 점과 나란히")에도 붙습니다(3D 뷰어 코어 0.21.0). 잠근 선 위 점 붙잡기 다음이고 모서리·면보다 먼저입니다.
예: 구멍 중심을 첫 점으로 찍고 다른 구멍 중심에 머문 뒤 → 키로 X축에 잠그면, 그 구멍의 Y축과 만나는 자리를 바로 찍습니다.
붙잡을 점 없이 판재의 구멍 벽·원형 포켓 벽·판 외곽선 둥근 모서리 같은 원기둥 벽을 가리켜도 벽을 나눈 삼각형 위가 아니라
커서 아래 원기둥 위 점을 찍습니다(3D 뷰어 코어 0.21.0 — 판 윗면·곧은 옆면 같은 평면은 그대로).
휴대폰은 길게 눌러 정밀 조준하는 동안 십자선을 점 위에 잠시 멈추면 같은 식으로 기억합니다(길게 누르기는 정밀 조준이 쓰므로 따로 두지 않았습니다).
손가락이 떨려 십자선이 멈추지 않으면 조준하는 동안 측정 띠에 나타나는 를 다른 손가락으로 눌러 십자선 아래 점을 바로 기억합니다(아래 5).
둘째 점을 찍거나 측정을 지우면 잊고, Esc는 첫 점을 찍기 전에도 기억한 점부터 지웁니다.
측정 도구는 파일을 열 때 3D 보기와 함께 받아 두므로 문서가 보이면 바로 켜집니다. 받지 못했으면(연결이 끊김 등) 측정 단추를 누를 때
다시 받습니다. 받는 동안 단추는 눌린 채 흐리게 보이고 상태 줄에 "측정 도구를 받는 중…"이라 적은 뒤, 받으면 측정을 켭니다.
받는 동안 단추를 다시 누르거나 Esc를 누르면 켜지 않습니다. 받지 못하면 연결이 끊겼을 때는 연결된 뒤 다시 누르라고,
연결이 되는데도 못 받으면(앱의 새 버전이 나와 예전 조각이 없음) 새로 고친 뒤 최근 문서에서 다시 열라고 알립니다.
숨긴 공구, 단면에 잘린 쪽, 재생 중 아직 그리지 않은 이동의 중심에는 붙지 않습니다(중심마다 처음 그린 이동 번호를 차례로 두고 재생 자리를
코어 붙잡기 한도로 줍니다 — 재생 중에도 중심 목록을 다시 주지 않습니다). 원호 중심이 아주 많아도 코어가 점 범위 나무로 커서 둘레만 보므로
마우스 이동이 거의 늦어지지 않습니다(중심 10만 개에서 이동 한 번 약 0.2 ms — 모두 훑으면 9 ms).
읽는 동안 덮개의 (또는 Esc)로 멈추면 열려 있던 문서를 그대로 둡니다.
그래픽 문맥을 잃으면(휴대폰에서 앱을 뒤로 보냈다 돌아올 때 등) 되찾을 때까지 3D 보기 가운데에 "그래픽을 다시 준비하는 중…"을 보입니다(3D 뷰어 코어 0.25.0).
해석이 스스로 정한 것(선반 프로그램으로 읽음 · 소수점 없는 큰 좌표가 많음 · 판별한 방언 Siemens·Haas 등)은 3D 보기 위(좁은 화면은 아래) 띠로 알리고,
띠의 단추로 바로 바꿔 다시 읽거나(밀링으로 읽기·최소 단위로 읽기) 정보 탭을 엽니다. ✕로 그 문서에서 닫습니다.
좁은 화면에서는 띠 글을 두 줄까지(띠가 둘 넘거나 재생 띠가 있으면 한 줄) 보이고 글을 누르면 펼치며, 잠깐 뜨는 알림은 아래쪽 띠들 위로 올라가 단추를 가리지 않습니다.| 키 | 하는 일 |
|---|---|
| F | 전체 보기 |
| 1~7 | 앞·뒤·왼쪽·오른쪽·위·아래·등각 |
| F7 | 격자 켜고 끄기 |
| Space | 재생 / 멈춤 |
| ← → | 한 이동씩 앞·뒤로(Shift는 10개). 판재 깎기에서는 한 가공씩(가공 하나씩 따라가기) |
| ↑ ↓ | 고른 줄을 앞·뒤로(3D 보기·코드 목록에 초점이 있을 때) |
| Shift(누르고 있기) | 측정 첫 점을 찍은 뒤: 지금 방향(축·나란함·수직, 첫 점에서 경로를 따라 가리키면 그 경로 방향, 원호(판재 윤곽·판 외곽선·포켓 둘레·구멍 테두리, 반경 보정 경로의 호 포함)·스플라인의 끝·가운데 점이나 그 위의 점에서 그 곡선 위를 가리키면 접선 방향) 잠금 |
| → ← ↑ · ↓ | 측정 첫 점을 찍은 뒤: X·Y·Z축으로 잠금 · 지금 방향 잠금(같은 키로 풀기) |
| Ctrl/⌘+F · Ctrl/⌘+G | 코드에서 찾기 · 다음 찾기 |
| Esc | 측정·고른 것 지우기(측정에서 끌어온 점을 기억했으면 그것부터, 읽는 중이면 읽기 멈추기) |
| Ctrl/⌘+O | 파일 열기 |
주소 인자: ?sample(예제 바로 열기), ?open=<같은 출처 주소>(그 파일을 받아 열기 — 자동 확인용, 다른 사이트 주소는 열지 않음).
공유 받기가 붙이는 ?recent=<id>·?share=<열쇠>·?share-error=<까닭>은 한 번만 쓰고 주소에서 지웁니다.
테마는 시스템 설정을 따르고, 제목 막대 단추로 고른 테마는 이 브라우저에 기억합니다.
자동 확인용으로 window.opencadApp(상태 state, open·openSample·openUrl·reparse·cancel·setSection 등)을 둡니다.
import { loadNcFile } from 'opencad-nc';
const { parsed, scene } = loadNcFile('part.nc', bytes); // 형식은 앞부분·확장자로 자동 판별
// parsed.kind: 'gcode' | 'heidenhain' | 'mpr' | 'pgi' | 'xml' (판재 형식은 parsed.part: MprPart)
// scene: Scene3d (opencad-viewer3d의 Viewer3d.setScene에 넘긴다)
형식별 함수(parseGcode, parseHeidenhain, parseMpr, parsePgi, parseKdtXml, ncToScene, mprToScene)와 확장자 목록(NC_EXTENSIONS 등), isPanelKind도 src/index.ts에서 내보냅니다.
선택 기능(기본은 모두 꺼짐 — 켜지 않으면 결과가 예전과 같습니다):
// 선반 모드('auto'면 글로 판별 — Fanuc 선반 G 코드 체계 latheSystem: 'A'|'B'|'C'도 고르지 않으면 판별, p.latheSystem),
// 회전축 감기, 반경 보정용 공구 표(키 = D 번호, LinuxCNC는 공구 번호)
const p = parseGcode(text, { lathe: true, rotary: { A: 'x', stepDeg: 2 }, toolTable: { 1: { diameter: 10 } } });
p.compRuns; // 보정 구간 [{ first, end, side, plane, radius, source, line }]
const scene = ncToScene(p, { compPath: true }); // 선분 묶음 'nc-comp'(묶음 '보정 경로'), meta.compRuns
const cp = ncCompPath(p, { radius: 5 }); // 조각(직선·호) 구조 배열 — compPathSegments(cp)로 선분
// 선반 공구 끝 R 보정: 노즈 반지름과 LinuxCNC 공구 방향(1~8이면 가상 공구 끝 경로, 9는 노즈 중심)
const t = parseGcode(text, { lathe: true, toolTable: { 1: { radius: 0.4, orientation: 3 } } }); // 또는 machine.latheToolsMm(mm, 파일에 없을 때만)
t.compRuns; // [{ …, radius: 0.4, tool: 1, orientation: 3, orientationSource: '공구 표 T1' }]
판재 장면(mprToScene)의 meta.lids는 포켓·홈 그물 id마다 뚜껑(들어가는 판 면 자리의 끝면) 삼각형 구간 { start, count }입니다.
3D 뷰어 코어 setItemPickThrough(id, [lid])에 넘기면 반투명 판 너머 고르기가 뚜껑을 지나 포켓 바닥·벽에서 멈춥니다(뷰어 앱이 그렇게 씁니다).
| 경로 | 내용 |
|---|---|
index.html, public/favicon.svg | 뷰어 앱 진입(제목·설명·테마 먼저 정하기, 홈 화면 앱 머리 태그) |
site/manifest.webmanifest, public/icon-*.png, public/apple-touch-icon.png | 홈 화면에 추가(웹 앱 매니페스트 원본·아이콘 — favicon.svg 그림을 Chrome으로 그려 만듦). 언어별 매니페스트와 파일 연결(share_target·file_handlers)은 tools/site.mjs 설정으로 공용 사이트 도구가 만든다 |
tools/site.mjs, tools/sw-source.mjs | 이 앱의 사이트 설정(앱 정보 원본·파일 연결·서비스 워커 판 이름 짓기 — vite.config.ts와 시험이 같이 씀)과 오프라인 서비스 워커 본문(미리 담은 것 쓰기·공유 받기). 언어 페이지·검색 정보·사이트맵·dist/sw.js는 공용 사이트 도구의 Vite 플러그인이 이 설정으로 만든다 |
site/i18n-core.mjs, site/i18n.mjs, site/locales/ | 공용 번역기에 이 앱의 사이트 정보(site/metadata.json)를 넘기고 사전을 넣는다 |
src/app/ | 뷰어 앱 화면: app.ts(틀·열기·보기·재생·키), panel.ts(코드·공구·정보·가공·변수 탭), doc.ts(문서 상태·요약 글), protocol.ts·payload.ts·client.ts(워커 메시지·이전 목록), format.ts·keys.ts·theme.ts(글·키·테마), settings.ts(기계·읽기 설정, localStorage), recent.ts(최근 문서·공유 받은 파일, IndexedDB), measure.ts(측정 안내 글·붙잡은 종류 이름 — 첫 화면), measure-tools.ts(문서를 열 때 받는 측정 도구 조각 입구: measure-lock.ts 측정 방향 잠금·잠금 단추·붙잡기 툴팁·코어 거리 글상자 비키기, measure-track.ts 측정 띠의 끌어온 점 단추, snap.ts 원호 중심·이동 가운데 점·판재 윤곽 붙잡기, aim.ts 정밀 조준, tangent.ts 원호(G2/G3·판재 윤곽 호)·스플라인 위 점의 접선 잠금, refine.ts 코어 수직 발·선분 위 점·교차점을 곡선 위로 다듬기와 코어에 주는 붙잡기 선택, intersect.ts 두 요소(원호·스플라인·직선)의 교차점(식·두 매개변수 뉴턴), curve.ts 곡선 위 점 찾기(구간 나눔 + 뉴턴), spline.ts 스플라인 계산), core.ts(3D 뷰어 코어·측정 도구를 문서를 열 때 받기, 받기 전에 측정을 켰을 때 받은 뒤 켜기), styles.css |
src/worker/parse.worker.ts | 해석 워커: 파일 확인 → 해석 → 장면·패널 자료를 이전 가능한 버퍼로 돌려줌 |
src/index.ts | 공개 입구: 형식 판별, , , , |
npm install
npm run dev # http://localhost:4401/ (예: /?sample)
npm run typecheck
npm test
npm run build # tsc --noEmit && vite build → dist/
npm run preview # 빌드 결과 보기
3D 뷰어 코어는 github:GyuTae-Kim/opencad-viewer3d#v0.28.0(잠금 커밋 c766d37)으로 받습니다. 보기에서 누르기는 0.26.0 onPick의 front로
판 너머 고르기와 가장 앞 고르기를 알림 하나에서 받고, 판재 포켓·홈 뚜껑은 0.27.0 setItemPickThrough로 판 너머 고르기에서 지나가게 합니다.
끌어온 점에서 나란함·수직은 0.28.0 inference.trackDirs의 edge 거르기(src/app/refine.ts의 trackDirEdge — 이동 번호·판재 붙잡기 표로 곡선을 나눈 현을 가름)로 켭니다.
0.26.0의 작은 법선 배열(packNormals)은 아직 쓰지 않습니다(아래 놓기와 같은 까닭으로 NC 장면의 법선이 작습니다). 0.25.0의 놓기(setScene의 release —
올린 뒤 법선·무늬 좌표·무늬 원본을 장면에서 지움)는 쓰지 않습니다. NC 장면에서 놓을 수 있는 것은 그물 법선뿐이고(공구 경로 선분은 위치·색이라 놓을 것이 없고
무늬는 없음) 대부분 깎기 시뮬레이션 소재 조각의 마디 법선입니다. 헤드리스 Chrome에서 GC 뒤 ArrayBuffer 뒷저장의 9% 아래였고(볼 엔드밀 30만 이동 1024칸 140.7 MB 가운데 12.4 MB,
자동 554칸 73.0 MB 가운데 3.6 MB, 공구 경로만이면 0, 판 2400 × 1200 MPR은 판 그물 법선 0.4 MB), 앱이 같은 조각 그물로 장면을 다시 짜 올리므로 놓으면 그때마다
워커에서 법선을 다시 받아야 합니다.
문맥 잃음·되찾음 알림만 써서 되찾을 때까지 3D 보기 가운데에 "그래픽을 다시 준비하는 중…"을 보입니다(장면은 코어가 그대로 다시 올립니다). 코어를 올리면
npm install "opencad-viewer3d@github:GyuTae-Kim/opencad-viewer3d#<태그>"로 받아 package.json·package-lock.json을 함께 바꿉니다(그냥 npm install은 옛 커밋을 남김).
표본·임시 파일은 .runtime/(git 무시)에 둡니다.
앱은 코어 입구(opencad-viewer3d)를 첫 문서를 열 때 src/app/core.ts의 import()로 받습니다(시작 화면 JS를 줄이려고 — 파일을 열기 시작하면
해석과 함께 받고, 오프라인 앱은 서비스 워커가 미리 담아 둡니다). 첫 화면 코드는 코어의 작은 모듈(opencad-viewer3d/camera·viewcube)만
값으로 가져오고 나머지는 import type으로만 씁니다(정밀 조준이 쓰는 pointer는 측정 도구 조각과 함께 받습니다).
측정 도구(src/app/measure-tools.ts 입구 — 방향 잠금·툴팁, 붙잡기 점, 정밀 조준, 끌어온 점 단추, 원호·스플라인 접선, 곡선 위로 다듬기)도 같은 때 core.ts의 import() 하나로 받습니다.
첫 문서는 코어를 받은 뒤 측정 도구를 3초까지 더 기다려 보이고(조각이 작아 보통 먼저 옵니다), 받지 못해도 문서는 엽니다.
앱·패널이 받기 전에도 쓰는 글(안내, 붙잡은 종류 이름)은 measure.ts에 둡니다. tests/app/core-split.test.ts가 main.ts에서 정적 가져오기로
닿는 모듈을 훑어 코어 입구·측정 도구가 첫 화면에 다시 들어오지 않는지 지킵니다.
첫 화면 JS는 끌어오기 요청의 휴대폰 성능 예산(tools/performance-budget.json — 전송 61,440바이트)에 걸리므로, 측정·깎기처럼 문서를 연 뒤에만 쓰는
코드는 이렇게 나눕니다. 지금 첫 화면 JS는 163 KB(gzip 58.3 KB, CI와 같은 opencad-site serve-performance 전송 58,652바이트 — 여유 약 2.8 KB)이고,
측정 도구 조각 47 KB(gzip 17.5 KB)와 코어 조각 267 KB(gzip 89 KB)는 파일을 열 때 받습니다(측정 도구를 나누기 전 첫 화면은 169 KB, gzip 60.7 KB, 전송 60,856바이트).
측정 방향 잠금 시험(tests/app/measure-lock-core.test.ts)은 jsdom에 가짜 WebGL2(tests/app/fake-webgl.ts)를 깔고 진짜 코어 뷰어에
포인터·키를 넣어, 앱의 툴팁·안내·잠금 단추가 코어 판정을 그대로 따르고 코어 거리 글상자가 툴팁·십자선·뷰 큐브·측정 띠를 비키는지 봅니다(그림은 그리지 않습니다).
끌어온 점 단추 시험(tests/app/measure-track-pick-core.test.ts)은 같은 진짜 코어에 앱의 정밀 조준을 붙여, 십자선이 떨려 머무르기가 걸리지 않아도
조준하는 동안 누른 단추가 십자선 아래 점을 기억하고 그 축에 붙는지 봅니다(코어 타이머는 가짜 시계로, 길게 누르기는 시간으로 기다립니다).
잠근 선과 끌어온 축의 교차(), 끌어온 점에서 모서리와 나란함·수직과 기준 현 거르기
(), 판 너머 구멍·포켓 벽(),
볼록한 원기둥 벽을 스치는 축()도 같은 진짜 코어로 봅니다.
보기에서 판 너머 가공 누르기()는 앱 전체를 진짜 코어로 띄우고 해석 워커만 같은 스레드로 바꿉니다.
포켓·홈 뚜껑 너머 재기와 누르기()는 뚜껑을 주지 않은 28차 판정과 견줍니다.
공용 사이트 도구: 다섯 언어 페이지·검색 정보·사이트맵·내용 수정일·언어별 앱 정보·서비스 워커 빌드, 공용 번역기, 언어 메뉴, 서비스 워커 등록·새 판 알림,
방문 통계, 휴대폰 성능 비교는 다른 OpenCAD 앱과 함께 쓰는 opencad-site-kit
(github:GyuTae-Kim/opencad-site-kit#v0.1.5, 잠금 커밋 43ec8da, 빌드 때만 써서 devDependencies)에 있습니다.
이 앱에만 있는 값은 tools/site.mjs(앱 정보 원본 site/manifest.webmanifest와 파일 연결, 검색 정보 자리의 앱 정보 링크, 서비스 워커 판 이름 짓기)와
tools/sw-source.mjs(서비스 워커 본문)에 둡니다. check:seo·check:i18n·test:seo·update:content-dates는 도구의 opencad-site 명령입니다.
도구 판을 올릴 때는 빌드 결과를 견줍니다.
npx vite build && cp -R dist .runtime/dist-before # 올리기 전
npm install -D "opencad-site-kit@github:GyuTae-Kim/opencad-site-kit#<태그>" # package.json·잠금 파일을 함께
npx vite build && npx opencad-site compare-dist .runtime/dist-before dist # 바뀐 파일과 처음 다른 곳
도구의 판 설명에 적힌 것 밖의 차이가 없으면, 스크립트가 바뀐 경우 내용 수정일을 갱신하고(아래 "검색·공유 메타데이터와 콘텐츠 수정일") 빌드·시험을 돌립니다.
실제 파일 훑기: 각자 가진 NC 파일 폴더를 앱 워커와 같은 해석 길로 읽어 예외·경고·빈 경로·시간을 모읍니다(파일을 바꾸거나 저장소에 넣지 않습니다).
npm run sweep -- --dirs "<폴더1>:<폴더2>" --out .runtime/sweep/before # 또는 OPENCAD_SWEEP_DIRS="<폴더1>:<폴더2>"
# --jobs 4(자식 프로세스 넷으로 나눠 해석) --limit 100(앞의 100개만) --force(같은 --out 덮어쓰기)
npm run sweep:compare -- .runtime/sweep/before .runtime/sweep/after --fail-on-change
폴더를 주지 않으면 쓰는 법만 찍고 끝납니다(CI에서는 돌지 않습니다). 앱이 여는 확장자만 고르고, 내용(sha256)이 같은 파일은 한 번만 읽으며
C 머리 파일(.h)은 뺍니다. <out>/records.jsonl에 파일마다 분류(ok·warn·empty·error)·형식·방언·지표(선분·이동·가공 수, 장면 서명 등)·경고 코드를,
summary.json에 분류·형식별 개수, 예외·경고 코드별 개수, 해석 시간 백분위, 품질 지표를 씁니다. 경로는 이 로컬 결과에만 남기고 보고·저장소에는 옮기지 않습니다.
sweep:compare는 같은 내용 해시끼리 분류·지표가 바뀐 파일과 새 예외를 보이고, --fail-on-change면 바뀐 것이 있을 때 1로 끝납니다.
깎기 시뮬레이션 재기: 합성 프로그램(볼 엔드밀 3D 곡면 지그재그·평 엔드밀 층 포켓·G83 구멍 격자·판 2400 × 1200 MPR)을 코드로 만들어 끝까지 깎는 시간과 메모리, 그물 크기 가운데 마디 법선(매끈한 음영)이 차지하는 몫을 표로 찍습니다.
npm run sim:bench -- --moves 300000 --res auto,auto-phone,1024,384 # --only raster,pocket,drill,panel
| 프로그램(30만 이동) | 격자 | 깎기 | 저장점 | 깎인 조각 그물(그 가운데 마디 법선) |
|---|---|---|---|---|
| 볼 엔드밀 3D 곡면 | 자동 553 × 553 | 2.5초 | 51 MB | 12 MB (3.6 MB) |
| 볼 엔드밀 3D 곡면 | 1024 × 1024 | 6.3초 | 70 MB | 41 MB (12 MB) |
| 볼 엔드밀 3D 곡면 | 휴대폰 자동 256 × 256 | 0.7초 | 2 MB | 2.6 MB (0.8 MB) |
| 평 엔드밀 포켓 | 1024 × 725 | 1.2초 | 7 MB | 20 MB (0) |
| 드릴 구멍 격자(48만 이동) | 자동 905 × 900 | 0.8초 | 39 MB | 32 MB (9.5 MB) |
| 판 2400 × 1200 MPR(이동 1,946개) | 자동 1024 × 512 | 0.01초 | 0 | 14 MB (0 — 평 끝 공구뿐) |
(2026-10-28, 개발 기기 Node 한 스레드에서 세 번 재어 가장 빠른 값 — 다른 작업이 함께 돌던 때라 조금 느릴 수 있습니다. 판의 그물 만들기는 약 0.02초입니다.
10차에 기계가 매우 바쁠 때(부하 평균 70 안팎) 다시 재면 깎기 시간은 1.11.6배였고, 깎는 코드는 바뀌지 않아 표의 시간은 그대로 둡니다. 그물 크기는 같습니다.
13차에 관통한 곳을 비우도록 그물 만들기를 바꾼 뒤 다시 재면(부하 평균 1143) 깎기 시간은 2.53.6배였고, 깎는 코드는 이때도 바뀌지 않았습니다.
저장점과 그물 크기(꼭짓점·색·법선)는 표와 같습니다. 판 MPR의 관통 구멍(Ø35 20개 — 깊이 25, 판 두께 19)처럼 판을 뚫은 곳은
이제 비워 그리고, 그런 조각(1024칸에서 58개)만 뺀 삼각형 번호를 따로 갖습니다. 판의 조각 그물 만들기를 바꾸기 전 코드와 번갈아 25번씩 재면
가장 빠른 값이 15 ms에서 1618 ms가 되었습니다.)
브라우저에서는 워커가 같은 코드로 계산하고, 화면은 소재가 처음 다 모였을 때만 문서 장면 + 소재 그물을 올린 뒤 바뀐 조각 그물만 바꿉니다(코어 setMesh).
조각이 거의 모두 바뀌었고(색·공구 바꾸기) 공구 경로가 작으면 장면 전체 한 번이 더 싸서 그쪽으로 올립니다(측정 중이면 그래도 조각만).
×100 재생 6초를 GPU가 있는 헤드리스 Chrome(Metal)에서 잰 값(앞 = 장면 전체를 다시 올리던 9차 코드, 같은 코어 0.11.0):
| 합성 볼 엔드밀 프로그램 | 격자 | 한 번 올리기(가운데값 · 최대) | 초당 올리기 | 6초 동안 GPU로 보낸 양 | 50 ms 넘는 프레임 |
|---|---|---|---|---|---|
| 10만 이동 | 944 × 944 | 3.4 · 5.7 ms → 0.8 · 1.8 ms | 12 → 60(매 프레임) | 1,512 → 634 MB | 0 → 0 |
| 30만 이동 | 554 × 554 | 1.4 · 2.5 ms → 0.4 · 1.0 ms | 12 → 60 | 873 → 345 MB | 0 → 0 |
| 100만 이동 | 309 × 309 | 4.2 · 116 ms → 0.3 · 1.0 ms | 11 → 60 | 1,591 → 192 MB | 2 → 0 |
두 쪽 모두 프레임은 16.7 ms(60 fps)였고, 화면(끝까지·40% 자리, 판 600 × 400 PGI 넓은 화면·375px)은 화소까지 같았습니다. CPU를 4배 늦추면(휴대폰 흉내, 30만 이동) 두 쪽 모두 메인 스레드가 가득 차 평균 약 37~40 fps였고, 새 쪽은 한 번 올리기 6.7 → 2.6 ms로 매 프레임 소재를 바꿉니다.
휴대폰 검사: 끌어오기 요청마다 mobile-validation.yml이 같은 실행기에서 기준(main)과 후보를 빌드해 Lighthouse로 휴대폰 첫 열기를 세 번씩 재고
(압축해서 주는 미리 보기 서버와 중앙값 표는 공용 도구의 opencad-site serve-performance·performance-summary — 기준 빌드 뒤 이 커밋으로 돌아와
npm ci를 다시 해 명령을 찾는다), playwright-cli로 390px 터치 화면에서 파일 고르기 → 도구 막대 → 원호 중심·가운데 점 측정 →
패널 → 최근 문서 → ?sample → 영어 페이지를 확인합니다(tools/mobile-browser-check.js).
끝으로 후보의 가운데값을 tools/performance-budget.json과 견줘(opencad-site check-performance-budget) 넘으면 실패합니다
(성능 90 이상, 접근성·SEO 100, FCP 2000 ms·LCP 2200 ms·TBT 300 ms 이하, 첫 화면 JS 60 KiB 이하 등 — 파일의 note에 잰 값).
main에 올리면 .github/workflows/deploy.yml이 형식 검사 → 시험 → 빌드 뒤 dist/를 gh-pages 가지에 게시합니다
(GitHub Pages: https://gyutae-kim.github.io/opencad-nc/). 끌어오기 요청은 ci.yml이 같은 확인을 하고, mobile-validation.yml이 휴대폰 첫 열기와 터치 흐름을 봅니다.
compPath)만 나옵니다(반지름을 공구 표·옵션에서 정하므로).
여러 급속 규칙은 설명서가 앞 급속이 어디로 가는지 적지 않아 보정 없는 프로그램 점으로 그립니다. 실제 LinuxCNC와 견줄 일이 남았습니다.
선반 공구 끝 R 보정의 공구 방향 번호는 LinuxCNC 그림 기준이라 Fanuc 등 다른 컨트롤러의 가상 공구 끝 번호와 견주지 않았고(경고), 방향 0의 뜻,
공구 마모 보정·공구 표의 앞뒤 각(I·J), 보정 중 공구 바꾸기는 다루지 않습니다[K·KO(면 좌표계), BohrUniv 등 나머지 매크로(지금은 목록과 위치 점만).
Easyedge·FreiFormTasche·Tasche의 RD는 실제 파일로 추정한 뜻이라 다른 프로그램이 쓴 파일(띠를 윤곽 여러 변에 걸치거나 MDA·MDE·RI를 바꾼 것,
TI가 양수인 자유 형상 포켓, 공구 반지름보다 작은 RD)에서는 다를 수 있습니다. 자유 형상 포켓·사각 포켓의 공구 경로(겹침 XY·나선 진입)는 그리지 않고 판 부분만 보입니다vertices 구간)는 쓰지 않습니다trackDirMeetSnap)만 있어 아직 붙지 않습니다. 곡선을 짧은 직선 이송(G1)으로 풀어 쓴 CAM 프로그램이나
회전축 감기로 나눈 경로의 토막은 프로그램에서 곧은 이동이라 나란함·수직의 기준이 됩니다(원호·스플라인 정의가 없어 가르지 못함)2dcad-viewer의 src/formats/nc/에서 분리했습니다(커밋 d7c584a 기준, 처음 들어온 것은 PR #7).
src/view/의 파일은 같은 커밋의 src/app/view3d/에서 옮겼습니다.
2D 뷰어 안에서 NC 파일을 3D 탭으로 열던 화면(코드 보기·공구 목록·재생 패널, 웹 워커 — 같은 커밋의 src/app/view3d/manager.ts·panel.ts 등)은
2D 화면 골격(리본·탭)에 기대지 않게 다시 짜서 src/app/·src/worker/의 혼자 돌아가는 뷰어 앱으로 옮겼습니다.
MIT
site/metadata.json을 공통 원본으로 삼아 공용 사이트 도구의 Vite 플러그인이 제목·설명·canonical·Open Graph·Twitter Card·JSON-LD를 초기 HTML에 넣습니다. 같은 원본으로 앱의 공개 주소를 담은 sitemap.xml도 생성합니다. 공유 이미지는 각 앱의 public/og-opencad.jpg이며 앱 파일과 함께 배포합니다. robots.txt는 같은 호스트 루트(https://gyutae-kim.github.io/robots.txt)의 설정이 적용됩니다.
site/content-history.json에는 실제 콘텐츠 수정 시각과 지문을 저장합니다. 초기 HTML 본문·메타데이터·공유 이미지와 빌드된 앱/워커 JavaScript·CSS를 비교하므로 앱 기능이 바뀌면 날짜 갱신이 필요합니다. 수정일 자체와 소스 맵은 비교에서 제외합니다.
npm run update:content-dates -- --at YYYY-MM-DDTHH:mm:ssZ를 실행합니다.npm run build로 다시 빌드합니다. 변경되지 않은 콘텐츠는 기존 날짜를 유지하며, 날짜가 오래되었거나 잘못된 경우 SEO 검사가 실패해 배포를 막습니다.npm run test:seo와 기존 테스트를 실행하고 메타데이터·수정일 기록을 함께 커밋합니다.일반 빌드나 배포 시각으로 날짜를 자동 갱신하지 않습니다. sitemap.xml의 lastmod와 앱 구조화 데이터의 dateModified는 동일한 기록을 사용합니다. 배포 뒤에는 앱·사이트맵·공유 이미지의 실제 공개 응답을 확인하고, 포트폴리오 통합 사이트맵의 연결 날짜도 새 앱 사이트맵과 맞춥니다. 사이트맵 게시와 검색엔진의 실제 수집·색인 완료는 별도로 확인합니다.
한국어 기본 주소와 영어(/en/), 일본어(/ja/), 중국어 간체(/zh-CN/), 독일어(/de/) 페이지를 제공합니다. 각 언어의 소개 본문과 검색·공유 메타데이터를 빌드할 때 생성하며, hreflang과 사이트맵이 같은 페이지의 언어들을 연결합니다. site/locales/*.json에 한국어 원문과 번역을 저장하고, 브라우저는 선택한 언어의 사전만 내려받습니다. 방문 중 외부 번역 서비스는 호출하지 않습니다.
우측 상단 지구본 아이콘에서 언어를 선택합니다. 방향키로 이동하고 Escape나 바깥 클릭으로 닫을 수 있습니다. 언어 선택은 URL에 반영하며, 열린 문서나 편집 상태를 다시 만들지 않고 화면 문구·접근성 설명·오류·도움말을 갱신합니다. 명령 식별자·입력값·파일명·모델 이름·원본 도면의 문자는 번역하지 않습니다. 새 데이터 이름을 표시하는 요소에는 translate="no"를 지정하고, UI 문구의 {0} 같은 변수 자리를 변경하지 마세요. 여러 줄 메시지와 새 한국어 문구도 번역 사전에 추가해야 합니다. 묶음 이름(급속 이송·구멍·공구 없음·가공 종류 등)은 앱 낱말이라 옮기고, 이름 붙은 공구의 묶음·변수 표의 이름·식·설명은 파일 자료라 그대로 둡니다. 코드 목록은 글을 옮기지 않지만 목록 이름과 경고 줄 번호 칸의 풍선 글(경고가 둘 넘으면 줄마다)은 옮깁니다. ·로 이은 문장은 짧은 변수 문구(예: 줄 {0})가 뒤 조각까지 그대로 잡으면 조각마다 따로 옮깁니다(남는 한국어가 적은 쪽을 고름 — 잡은 파일·공구 이름은 여전히 그대로).
변수 자리가 잡은 값은 옮기지 않으므로, 해석기가 이유 글을 끼우는 경고(예: Heidenhain FK의 "…을 정할 자료가 모자람 — 다음 블록 자료가 필요한 FK는 그리지 않음",
MPR 윤곽 모서리 둥글리기·모따기)와 정보 탭 목록에 글 하나로 들어가는 보정 경로 경고("보정 경로: N번 줄: …")는 이유마다 전체 꼴을 사전에 둡니다
(tests/warn-i18n.test.ts가 네 언어에서 한국어가 남지 않는지 봅니다).
npm run test:i18n은 언어 전환과 문서 데이터 보존을, npm run check:i18n은 다섯 언어의 정적 소개·canonical·언어 링크·사전 변수·에셋 경로를 검사합니다. 번역이나 앱을 수정하면 기존 콘텐츠 수정일 갱신 절차를 따른 뒤 다시 빌드합니다. 자동 번역 초안의 주요 메뉴와 제품명은 교정했으며, 긴 기술 설명에는 추가 원어민 감수가 필요합니다.
npm run sim:benchsetMeasureTrackPoints — 최근 셋까지, 첫 점 전에도).
단추에 기억한 점 수를 적고, 기억한 점이 있는 동안은 조준하지 않아도 단추가 남습니다. 붙잡은 점이 없으면(축 위·빈 곳) 띠 위에 맞추는 법을 알리고,
기억한 점은 "다시 재기"로 지웁니다. 조준하지 않을 때는 단추가 없어 측정 띠가 예전과 같고, 넓은 화면(마우스)은 측정 띠가 없어 머무르기만 씁니다.
측정 중 한 손가락을 거의 움직이지 않고(10화소 안) 길게(0.35초) 누르면 손가락 위 64px에 십자선이 떠 정밀 조준합니다 — 끌면 십자선이 따라가고
(붙잡을 점·거리를 미리 보입니다) 떼면 그 자리를 누른 것으로 칩니다. 십자선 자리는 보통 터치 누르기(14화소)가 아니라
마우스와 같은 좁은 허용 거리(선분 6화소, 끝점 10화소 — 코어에 펜 포인터로 알림)로 골라, 구멍·선분이 빽빽한 곳에서도 미리 본 점이 그대로 찍힙니다.
조준하는 동안 거리 글상자는 십자선과 손가락을 비켜 그려, 첫 점 가까이를 가리켜도 거리가 가려지지 않습니다.
휴대폰에서는 거리 글상자가 화면 폭의 절반을 넘어, 두 점이 오른쪽 위면 뷰 큐브 밑에, 아래쪽이면 측정 띠 밑에 그려졌습니다. 지금은 둘을 비켜 그립니다.
둘째 손가락이 닿으면 조준을 그만두고 두 손가락 이동·확대로 넘어갑니다.
손가락 기기의 안내에는 키(Esc) 설명 대신 길게 누르기를 적고, 파일 고르기 상자는 확장자로 거르지 않습니다
(iOS에서 .nc·.mpr 등이 흐리게 막히지 않게 — 모르는 확장자는 앞부분을 보고 이진 파일을 바로 막고, 글 파일은 내용으로 형식을 가립니다).opencad-nc)에 새것부터 8개까지 두고
(60MB 넘는 파일, 예제, ?open=으로 받은 파일은 두지 않음), 시작 화면의 최근 문서에서 이름·크기·연 시각을 보고 눌러 다시 엽니다.
휴대폰 브라우저가 뒤로 간 탭을 버렸다가 다시 읽어도 파일 앱을 다시 거치지 않습니다. 여러 파일 작업은 이은 글 하나를 주 프로그램 이름으로 두고
("파일 N개 작업"), 다시 열면 같은 작업으로 읽습니다. 줄마다 ✕(그 항목만 지우기)와 모두 지우기가 있고,
서버로는 보내지 않습니다(비공개 창처럼 저장소를 못 쓰면 목록이 생기지 않습니다). 읽는 동안 덮개(취소 가능)를 보이고,
저장된 내용이 없으면 그 항목을 지우되 잠깐 못 읽었으면(연결 끊김 등) 항목을 두고 다시 눌러 보라고 알립니다.
iOS에서 홈 화면에 추가한 앱은 Safari와 저장 공간이 따로라, Safari에서 연 최근 문서는 홈 화면 앱에 보이지 않습니다(반대도 같음).site/manifest.webmanifest — 이름 "OpenCAD NC 뷰어", 짧은 이름 "NC 뷰어", standalone)와
192·512·maskable 512 아이콘, iOS용 180px apple-touch-icon.png로 휴대폰 홈 화면에서 주소 막대 없이 앱처럼 엽니다.
빌드가 manifest.webmanifest와, 번역해 만든 <언어>/manifest.webmanifest(그 언어 페이지에서 시작)를 씁니다.
홈 화면 앱에서는 제목 막대를 상태 막대(노치) 아래로 내리고,
제목·상태 막대를 아래로 당겨도 페이지를 새로 읽지 않습니다(overscroll-behavior-y: none — 코드 목록·패널·시작 화면은 그대로 굴러갑니다).dist/sw.js — 본문 tools/sw-source.mjs, 목록·판 이름은 공용 사이트 도구)가 앱 껍데기(언어 페이지·앱 정보·아이콘)와 빌드 결과(해석 워커·번역 사전, 문서를 열 때 받는 3D 보기·측정 도구 조각 포함)를
미리 담아, 신호 없는 현장에서도 한 번 열어 본 기기라면 앱이 열리고 최근 문서를 엽니다. HTML은 네트워크 먼저(배포가 바로 보이게, 안 되면 담아 둔 것),
미리 담은 파일과 빌드 결과(assets/)는 담아 둔 것 먼저이고, 그 밖(?open=으로 받는 NC 파일 등)과 앱 범위 밖 주소는 서비스 워커가 손대지 않습니다
(바뀐 파일이 옛것으로 열리거나 큰 파일이 쌓이지 않게). 새 판이 설치되면 "새 버전이 있습니다 · 새로 고침" 알림을 띄우고,
누르면 새 판으로 바꿔 한 번 다시 읽습니다(판 이름은 담는 파일들의 내용 지문).
공유 받기(Android): 앱 정보의 share_target(POST …/share-target)으로 메일·메신저·파일 앱의 "공유 → NC 뷰어"를 받습니다.
파일 하나(60MB 이하)는 서비스 워커가 최근 문서(IndexedDB)에 넣고 그 언어 페이지의 ?recent=<id>로 엽니다.
여러 파일(주 프로그램과 부프로그램)이나 60MB 넘는 파일은 같은 저장소의 한 번 쓸 자리에 두고 ?share=<열쇠>로 열어,
앱이 읽자마자 지우고 여러 파일은 한 작업으로 이어 읽습니다(이은 작업은 파일을 고른 것처럼 최근 문서에 남음). 받지 못하면 까닭을 알립니다.
데스크톱 파일 열기: file_handlers(뷰어가 여는 확장자 전부 — NC_EXTENSIONS에서 빌드 때 만듦)와 launchQueue로 설치한 앱을
파일 탐색기의 "연결 프로그램"으로 쓸 수 있고, 여러 파일을 함께 고르면 창 하나에서 한 작업으로 엽니다(launch_type: single-client,
이미 열린 창이 있으면 그 창으로). 앱 정보에는 id를 두지 않습니다(언어 페이지마다 start_url이 id). 개발 서버는 서비스 워커를 등록하지 않습니다.parseNcFilencParsedToSceneloadNcFilecombineNcFilessrc/job.ts | 여러 파일을 한 작업으로 잇기·나누기(표지 줄, 주 프로그램 고르기) |
src/extensions.ts | 확장자 목록(NC_EXTENSIONS 등)과 판재 형식 갈래(isPanelKind) — 화면 코드가 해석기 없이 가져다 쓴다 |
src/gcode.ts, src/expr.ts | G-code 해석(좌표 변환·선반·회전축 포함)과 변수·식 |
src/siemens.ts | Siemens Sinumerik 블록·식·흐름·사이클·프레임(이동은 gcode.ts의 Runner를 그대로 씀, parseGcode에 방언으로 등록) |
src/comp.ts | 반경 보정 공구 중심 경로(옮긴 요소, 모서리 교점·호 잇기, 들어가기·나오기)와 선반 가상 공구 끝으로 옮기기(LATHE_TIP — LinuxCNC 공구 방향) |
src/heidenhain.ts | Heidenhain 평문 |
src/mpr.ts | woodWOP MPR(판재 가공 모델 MprPart — PGI·XML도 이 꼴로 읽는다) |
src/wangs.ts, src/xml.ts | WangShi PGI·KDT XML, 줄 번호를 남기는 작은 XML 읽기 |
src/model.ts, src/emitter.ts, src/text.ts | 공통 이동 모델·시간표, 이동 만들기, 원문 줄 |
src/corner.ts | 모서리 다듬기 기하(직선·원호 모서리의 둥글리기·모따기 — G-code RND·CHF·CHR과 MPR KR·KF가 함께 씀) |
src/lathe-g7x.ts | LinuxCNC 선반 G70~G72 윤곽 기하(모서리 A·C, 한 방향 검사, 떨어진 곡선, 거친 깎기 층·포켓) |
src/scene.ts, src/mesh.ts | 공구 경로·공작물 → Scene3d |
src/view/ | 화면 도우미: 재생 계산(playback.ts), 합성 예제 프로그램(sample.ts), 가상 목록(vlist.ts, vlist.css) |
src/sim/, src/worker/sim.worker.ts, src/app/sim.ts | 소재 깎기 시뮬레이션: 높이 지도와 공구·영역 내리기(grid.ts), 공구 단면·파일 값·사용자 표(tool.ts), 이동을 차례로 내리는 엔진과 저장점(engine.ts), 조각 그물·마디 법선·관통한 곳 비우기(mesh.ts), 3축 확인·소재 기본값·해상도(setup.ts), 판재 재료(panel-source.ts — 해석 워커가 가공 목록에서 윗면 가공을 골라 화면 자료로)와 판재 이동·공구 보정·가공 차례(panel.ts), 워커 몸통(runner.ts)과 메시지(protocol.ts). 화면(app/sim.ts — 깎기 탭, 장면 합치기)은 켤 때 나눠 받는다 |
tests/, tests/app/ | 해석기 시험, 앱 순수 로직 시험(글·키·테마·이전 목록·문서 모델) |
scripts/sweep/ | 실제 파일 훑기(npm run sweep)와 두 결과 비교(npm run sweep:compare) |
scripts/sim/bench.ts | 깎기 시뮬레이션 성능 재기(npm run sim:bench — 합성 프로그램 수십만 이동) |
.github/workflows/mobile-validation.yml, tools/mobile-browser*, tools/check-browser-result.mjs, tools/mobile-part.nc, tools/performance-budget.json | 끌어오기 요청마다 휴대폰 첫 열기 성능(Lighthouse, 예산 검사)과 터치 사용 흐름(playwright-cli) 확인 |
tests/app/measure-lock-track-core.test.tstests/app/measure-track-dir-core.test.tstests/app/measure-face-core.test.tstests/app/measure-face-open-core.test.tstests/app/pick-through-core.test.tstests/app/pocket-lid-core.test.tstests/app/measure-apex-core.test.ts