- 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
113 lines
4.0 KiB
TypeScript
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;
|
|
}
|