/** 그리기 명령 — 폴리선·호·다각형·타원·스플라인·점 (조사표 1절) */ import { CircleEntity } from '../../entities/CircleEntity'; import type { Entity } from '../../entities/Entity'; import { arcThroughThreePoints, ellipsePoints, regularPolygonPoints, splinePoints, } from '../../helpers/geometry/shape-points'; import { addEntities, getActiveLayerId } from '../../state'; import { Tool } from '../../tools'; import { arcEntity, lineEntity, pointEntity, polyLineEntity, styled } from '../factories/entity-factory'; import { createSequenceTool } from '../factories/sequence-tool'; export const plineToolStateMachine = createSequenceTool({ tool: Tool.PLINE, steps: [ { kind: 'point', instructions: '폴리선의 시작점을 지정하십시오.' }, { kind: 'point', instructions: '다음 점을 지정하십시오. ENTER로 종료합니다.' }, ], repeatLastStep: true, preview: (input) => { const preview = polyLineEntity([...input.points(), input.cursor]); return preview ? [preview] : []; }, commit: (input) => { const polyline = polyLineEntity(input.points()); if (polyline) addEntities([polyline], true); }, }); export const arcToolStateMachine = createSequenceTool({ tool: Tool.ARC, steps: [ { kind: 'point', instructions: '호의 시작점을 지정하십시오.' }, { kind: 'point', instructions: '호가 지나갈 두 번째 점을 지정하십시오.' }, { kind: 'point', instructions: '호의 끝점을 지정하십시오.' }, ], preview: (input) => { const points = input.points(); if (points.length === 1) return [lineEntity(points[0], input.cursor)]; if (points.length === 2) { const definition = arcThroughThreePoints(points[0], points[1], input.cursor); return definition ? [arcEntity(definition)] : [lineEntity(points[0], input.cursor)]; } return []; }, commit: (input) => { const [start, through, end] = input.points(); const definition = arcThroughThreePoints(start, through, end); // 세 점이 일직선이면 호가 성립하지 않으므로 선으로 대체한다 addEntities([definition ? arcEntity(definition) : lineEntity(start, end)], true); }, }); export const polygonToolStateMachine = createSequenceTool({ tool: Tool.POLYGON, steps: [ { kind: 'number', instructions: '면의 수를 입력하십시오 <6>.', defaultValue: 6 }, { kind: 'point', instructions: '다각형의 중심을 지정하십시오.' }, { kind: 'point', instructions: '꼭짓점을 지정하십시오 (중심에서의 반지름).' }, ], preview: (input) => { const points = input.points(); if (points.length !== 1) return []; const polygon = polyLineEntity(regularPolygonPoints(points[0], input.cursor, input.number(0))); return polygon ? [polygon] : []; }, commit: (input) => { const [center, vertex] = input.points(); const polygon = polyLineEntity(regularPolygonPoints(center, vertex, input.number(0))); if (polygon) addEntities([polygon], true); }, }); export const ellipseToolStateMachine = createSequenceTool({ tool: Tool.ELLIPSE, steps: [ { kind: 'point', instructions: '타원의 중심을 지정하십시오.' }, { kind: 'point', instructions: '장축의 끝점을 지정하십시오.' }, { kind: 'point', instructions: '단축 거리를 지정하십시오.' }, ], preview: (input) => { const points = input.points(); if (points.length === 1) return [lineEntity(points[0], input.cursor)]; if (points.length === 2) { const minor = points[0].distanceTo(input.cursor)[0]; const ellipse = polyLineEntity(ellipsePoints(points[0], points[1], minor)); return ellipse ? [ellipse] : []; } return []; }, commit: (input) => { const [center, majorPoint, minorPoint] = input.points(); const minorRadius = center.distanceTo(minorPoint)[0]; const ellipse = polyLineEntity(ellipsePoints(center, majorPoint, minorRadius)); if (ellipse) addEntities([ellipse], true); }, }); export const splineToolStateMachine = createSequenceTool({ tool: Tool.SPLINE, steps: [ { kind: 'point', instructions: '스플라인의 첫 점을 지정하십시오.' }, { kind: 'point', instructions: '다음 점을 지정하십시오. ENTER로 종료합니다.' }, ], repeatLastStep: true, preview: (input) => { const preview = polyLineEntity(splinePoints([...input.points(), input.cursor])); return preview ? [preview] : []; }, commit: (input) => { const spline = polyLineEntity(splinePoints(input.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: '도넛의 중심을 지정하십시오.' }, ], 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[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; }