Files
Aislo/B07_DesignDetail/openwebcad/src/tools/draw/basic-draw-tools.ts
T
eomsangdonandClaude Opus 5.5 3cebb37ed0 feat(CAD): 337-3a 나-1 그리기 8 — 오토캐드 물음 차례 · 옵션 · 기본값 · 빈 Enter · Esc · 끝난 뒤
- LINE: 「첫 번째 점 지정」 빈 Enter = 마지막 끝점에서 이음 · U = 마지막 토막(되돌림 한 칸) · 둘째 토막부터 C · Enter · Esc = 끝(토막 남음)
- PLINE(새 pline-tool): 호(A) 접선 호 · 길이(L) · 닫기(C · 호 모드 CL) · 명령 취소(U) · 선(L) · Esc · Enter = 그린 데까지 남김
- RECTANG: 치수(D) 길이 · 폭 기억(RECT.L · RECT.W) → 누른 쪽 사분면 · 없는 옵션 안내
- CIRCLE: 3P · 2P · 지름(D) <2r> · 반지름 기억(CIRCLERAD · 처음 <> 없음) · 한 번 뒤 끝
- POLYGON(새 polygon-tool): 변 수 기억 <4> · 3~1024 정수 · 모서리(E) · 내접/외접 기억 · 숫자 = 아래 변 수평
- ARC(새 arc-tool): 세 점 · 중심(C) · 각도(A) · 끝(E) · 반지름(R)
- ELLIPSE(새 ellipse-tool): 축 두 끝 · 중심(C) · 회전(R) · 다른 축 숫자 = 중심에서 반 길이(N10)
- SPLINE: 다음 점 입력 · U · 넷째 점부터 C · Esc 버림 · DONUT 중심 되풀이(loopFrom)
- 시험 draw-acad 22 · keyboard-chain 옛 선 · 폴리선 줄 두 곳 새 동작으로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmC34fsxxbweEG3RUjs3Mn
2026-10-16 00:16:15 +09:00

113 lines
4.0 KiB
TypeScript

/** 그리기 명령 — 스플라인·점·도넛 + 폴리선·호·다각형·타원 다시 내보냄 (조사표 1절 · 337 설계 1-1) */
import { CircleEntity } from '../../entities/CircleEntity';
import type { Entity } from '../../entities/Entity';
import type { Point } from '@flatten-js/core';
import { OPT } from '../../commands/prompt';
import { splinePoints } from '../../helpers/geometry/shape-points';
import { addEntities, getActiveLayerId } from '../../state';
import { Tool } from '../../tools';
import { pointEntity, polyLineEntity, styled } from '../factories/entity-factory';
import { createSequenceTool } from '../factories/sequence-tool';
import { option } from './draw-steps';
export { arcToolStateMachine } from './arc-tool';
export { ellipseToolStateMachine } from './ellipse-tool';
export { plineToolStateMachine } from './pline-tool';
export { polygonToolStateMachine } from './polygon-tool';
/** 닫힌 스플라인 — 끝 둘레 이웃을 이어 붙여 그린 뒤 이음 구간만 남김 */
function closedSplinePoints(points: Point[]): Point[] {
const spans = 12;
const wrapped = splinePoints([points[points.length - 1], ...points, points[0], points[1]], spans);
const loop = wrapped.slice(spans, spans * (points.length + 1));
return [...loop, loop[0]];
}
const NEXT_POINT = '다음 점 입력';
const TOLERANCE = option('TOLERANCE', 'L', '공차');
/**
* 스플라인(SPLINE) — 337 설계 1-1. 「첫 번째 점 지정:」 → 「다음 점 입력:」 → 「다음 점 입력 또는 [명령 취소(U)]:」 →
* 넷째 점부터 [명령 취소(U)/닫기(C)] 되풀이 · 빈 Enter = 확정 · C = 닫고 끝 · Esc = 버림.
*/
export const splineToolStateMachine = createSequenceTool({
tool: Tool.SPLINE,
steps: [
{
kind: 'point',
prompt: {
text: '첫 번째 점 지정',
absent: [
option('METHOD', 'M', '방법'),
option('KNOTS', 'K', '매듭'),
option('OBJECT', 'O', '객체'),
],
},
},
{
kind: 'point',
prompt: {
text: NEXT_POINT,
absent: [option('TANGENT', 'T', '시작 접촉부'), TOLERANCE],
},
},
{
kind: 'point',
prompt: (input) => ({
text: NEXT_POINT,
options: input.points().length >= 3 ? [OPT.UNDO, OPT.CLOSE] : [OPT.UNDO],
absent: [option('TANGENT', 'T', '끝 접촉부'), TOLERANCE],
}),
options: { UNDO: 'undo', CLOSE: 'close' },
},
],
repeatLastStep: true,
preview: (input) => {
const preview = polyLineEntity(splinePoints([...input.points(), input.cursor]));
return preview ? [preview] : [];
},
commit: (input) => {
const points = input.points();
const spline = polyLineEntity(input.closed ? closedSplinePoints(points) : splinePoints(points));
if (spline) addEntities([spline], true);
},
});
export const pointToolStateMachine = createSequenceTool({
tool: Tool.POINT,
steps: [{ kind: 'point', instructions: '점의 위치를 지정하십시오.' }],
preview: (input) => [pointEntity(input.cursor)],
commit: (input) => {
addEntities([pointEntity(input.point(0))], true);
},
});
/** 도넛 — 안쪽·바깥쪽 지름을 받아 동심원 두 개를 놓는다 */
export const donutToolStateMachine = createSequenceTool({
tool: Tool.DONUT,
steps: [
{ kind: 'number', instructions: '도넛의 내부 지름을 입력하십시오 <1>.', defaultValue: 1 },
{ kind: 'number', instructions: '도넛의 외부 지름을 입력하십시오 <2>.', defaultValue: 2 },
{ kind: 'point', instructions: '도넛의 중심을 지정하십시오.' },
],
loopFrom: 2, // 오토캐드 — 중심 되풀이 · 빈 Enter 끝
preview: (input) => donutEntities(input.number(0), input.number(1), input.cursor),
commit: (input) => {
addEntities(donutEntities(input.number(0), input.number(1), input.point(2)), true);
},
});
function donutEntities(
innerDiameter: number,
outerDiameter: number,
center: Parameters<typeof pointEntity>[0]
): Entity[] {
const circles: Entity[] = [];
for (const diameter of [innerDiameter, outerDiameter]) {
if (diameter > 0) {
circles.push(styled(new CircleEntity(getActiveLayerId(), center, diameter / 2)));
}
}
return circles;
}