/** 해치·그라데이션·경계·영역 — 선택한 객체가 이루는 닫힌 경계를 채우거나 뽑는다 */ import type { Point } from '@flatten-js/core'; import { toast } from 'react-toastify'; import { autoHatchSpacing, getHatchAngle, getHatchSpacing, getHatchStyle, } from '../../commands/draw-settings'; import { entitiesToLoop } from '../../helpers/geometry/entity-loop'; import { addEntities, getActiveLineColor, setSelectedEntityIds } from '../../state'; import { Tool } from '../../tools'; import { hatchEntity, polyLineEntity } from '../factories/entity-factory'; import { createSequenceTool, type SequenceInput } from '../factories/sequence-tool'; /** 선택 객체에서 닫힌 경계를 얻는다. 못 얻으면 안내 후 빈 배열 */ function loopFromSelection(input: SequenceInput): Point[] { const loop = entitiesToLoop(input.entities(0)); if (loop.length < 3) { toast.warn('닫힌 경계를 만들 객체를 선택하십시오.'); return []; } return loop; } function boundingSize(points: Point[]): { width: number; height: number } { const xs = points.map((point) => point.x); const ys = points.map((point) => point.y); return { width: Math.max(...xs) - Math.min(...xs), height: Math.max(...ys) - Math.min(...ys) }; } export const hatchToolStateMachine = createSequenceTool({ tool: Tool.HATCH, helpers: false, steps: [ { kind: 'selection', instructions: '해치를 채울 경계 객체를 선택한 뒤 ENTER를 누르십시오.' }, ], commit: (input) => { const loop = loopFromSelection(input); if (!loop.length) return; const { width, height } = boundingSize(loop); addEntities( [ hatchEntity(loop, { style: getHatchStyle(), color: getActiveLineColor(), spacing: getHatchSpacing() ?? autoHatchSpacing(width, height), angle: getHatchAngle(), }), ], true ); setSelectedEntityIds([]); }, }); export const gradientToolStateMachine = createSequenceTool({ tool: Tool.GRADIENT, helpers: false, steps: [{ kind: 'selection', instructions: '그라데이션을 넣을 경계 객체를 선택한 뒤 ENTER.' }], commit: (input) => { const loop = loopFromSelection(input); if (!loop.length) return; addEntities( [ hatchEntity(loop, { style: 'gradient', color: getActiveLineColor(), color2: '#000000', }), ], true ); setSelectedEntityIds([]); }, }); export const boundaryToolStateMachine = createSequenceTool({ tool: Tool.BOUNDARY, helpers: false, steps: [{ kind: 'selection', instructions: '경계를 뽑을 객체를 선택한 뒤 ENTER를 누르십시오.' }], commit: (input) => { const loop = loopFromSelection(input); if (!loop.length) return; const boundary = polyLineEntity(loop); if (boundary) { addEntities([boundary], true); toast.success('닫힌 폴리선 경계를 만들었습니다.'); } setSelectedEntityIds([]); }, }); export const regionToolStateMachine = createSequenceTool({ tool: Tool.REGION, helpers: false, steps: [{ kind: 'selection', instructions: '영역으로 만들 객체를 선택한 뒤 ENTER.' }], commit: (input) => { const loop = loopFromSelection(input); if (!loop.length) return; const region = polyLineEntity(loop); if (region) { addEntities([region], true); // 2D 웹 CAD에는 별도 영역 객체가 없다 — 닫힌 폴리선이 같은 역할을 한다 toast.info('영역은 닫힌 폴리선으로 작성했습니다.'); } setSelectedEntityIds([]); }, });