feat(B07 CAD): 337-4b 다-2 늘이기 · 재기 · 치수 오토캐드 조작 — LENGTHEN · DIVIDE · MEASURE · DIST · DIM 류

- LENGTHEN 새 도구(lengthen-tool.ts) — 측정할 객체 [증분(DE)/퍼센트(P)/전체(T)] <마지막 방식> · 누름 = 현재 길이 알림 · 방식별 값 기억 · 변경할 객체 되풀이 · U = 이 명령 안 바꿈만 · 빈 Enter · Esc 끝 · 선 · 호
- DIVIDE · MEASURE — 기본값 없음 · 정수 2~32767 · 양수 · 블록(B) 없음 안내 · MEASURE 누른 끝부터
- DIST — 오토캐드 꼴 두 줄 기록(거리 · 각도 · 증분) · 다중 점(M) 없음 안내
- DIMLINEAR · DIMALIGNED — 빈 Enter = 객체 선택 · [문자(T)/수평(H)/수직(V)] · 치수 문자 = 기록 · 치수 객체 문자 바꿈 칸(<> = 잰 값)
- DIMCONTINUE · DIMBASELINE — 되풀이 · 명령 취소(U) · 빈 Enter 끝 · DIMRADIUS · DIMDIAMETER [문자(T)] · 그 밖 치수 물음 글
- 시험 measure-acad 11

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014utrjfoW831Vc5wJUd13kL
This commit is contained in:
2026-10-16 00:15:35 +09:00
co-authored by Claude Opus 5.5
parent 7c35341002
commit c15be5116a
8 changed files with 1022 additions and 148 deletions
@@ -1,6 +1,12 @@
/** 반지름·지름·각도·호길이·세로좌표 치수와 중심 표식 (조사표 5절) */
/**
* 반지름·지름·각도·호길이·세로좌표 치수와 중심 표식 (조사표 5절).
* 오토캐드 물음 차례(337 설계 1-2) — DIMRADIUS · DIMDIAMETER = 「호 또는 원 선택:」 →
* 「치수선의 위치 지정 또는 [문자(T)]:」 · 한 번 뒤 끝 · 그 밖 치수 = 물음 글만.
*/
import { Arc, Circle, Point } from '@flatten-js/core';
import { toast } from 'react-toastify';
import { OPT, type OptionSpec } from '../../commands/prompt';
import { logLine } from '../../commands/run-command';
import { getDimDecimals } from '../../commands/dim-settings';
import type { Entity } from '../../entities/Entity';
import { MeasurementEntity } from '../../entities/MeasurementEntity';
@@ -8,7 +14,7 @@ import { polylineLength, sampleEntityPoints } from '../../helpers/geometry/sampl
import { addEntities, getEntities, setEntities, setSelectedEntityIds } from '../../state';
import { Tool } from '../../tools';
import { arcEntity, lineEntity } from '../factories/entity-factory';
import { createSequenceTool } from '../factories/sequence-tool';
import { createSequenceTool, type SequenceInput } from '../factories/sequence-tool';
import { annotationText, arrowHead, dimensionText } from './annotation.helpers';
interface CircularShape {
@@ -24,7 +30,24 @@ function circularOf(entity: Entity): CircularShape | null {
return null;
}
function radialDimension(entity: Entity, textPoint: Point, diameter: boolean): Entity[] | null {
/** 잰 값 글(「R10.00」 · 「Ø20.00」) */
function radialLabel(entity: Entity, diameter: boolean): string | null {
const circular = circularOf(entity);
if (!circular) return null;
const value = diameter ? circular.radius * 2 : circular.radius;
return `${diameter ? 'Ø' : 'R'}${value.toFixed(getDimDecimals())}`;
}
/** 바꾼 글(`<>` = 잰 값) · 없으면 잰 값 */
const labelWith = (measured: string, text?: string) =>
text && text !== '<>' ? text.replaceAll('<>', measured) : measured;
function radialDimension(
entity: Entity,
textPoint: Point,
diameter: boolean,
text?: string
): Entity[] | null {
const circular = circularOf(entity);
if (!circular) return null;
@@ -43,52 +66,71 @@ function radialDimension(entity: Entity, textPoint: Point, diameter: boolean): E
return [
lineEntity(start, textPoint),
arrowHead(edge, circular.center),
dimensionText(
diameter ? circular.radius * 2 : circular.radius,
textPoint,
diameter ? 'Ø' : 'R'
),
text && text !== '<>'
? annotationText(labelWith(radialLabel(entity, diameter) as string, text), textPoint)
: dimensionText(
diameter ? circular.radius * 2 : circular.radius,
textPoint,
diameter ? 'Ø' : 'R'
),
];
}
export const dimRadiusToolStateMachine = createSequenceTool({
tool: Tool.DIMRADIUS,
steps: [
{ kind: 'entity', instructions: '반지름을 기입할 원 또는 호를 선택하십시오.' },
{ kind: 'point', instructions: '치수 문자 위치를 지정하십시오.' },
],
commit: (input) => {
const parts = radialDimension(input.entity(0), input.point(1), false);
if (!parts) {
toast.warn('원 또는 호를 선택하십시오.');
return;
}
addEntities(parts, true);
},
});
const MTEXT: OptionSpec = { key: 'MTEXT', kw: 'M', en: 'MTEXT', label: '여러 줄 문자' };
const ANGLE: OptionSpec = { key: 'ANGLE', kw: 'A', en: 'ANGLE', label: '각도' };
export const dimDiameterToolStateMachine = createSequenceTool({
tool: Tool.DIMDIAMETER,
steps: [
{ kind: 'entity', instructions: '지름을 기입할 원 또는 호를 선택하십시오.' },
{ kind: 'point', instructions: '치수 문자 위치를 지정하십시오.' },
],
commit: (input) => {
const parts = radialDimension(input.entity(0), input.point(1), true);
if (!parts) {
toast.warn('원 또는 호를 선택하십시오.');
return;
}
addEntities(parts, true);
},
});
/** DIMRADIUS · DIMDIAMETER — 원 · 호 → [문자(T)] → 위치 */
function radialTool(tool: Tool, diameter: boolean) {
const measured = (input: SequenceInput) =>
input.has('obj') ? radialLabel(input.get<Entity>('obj'), diameter) : null;
return createSequenceTool({
tool,
steps: [
{ kind: 'entity', id: 'obj', prompt: { text: '호 또는 원 선택' } },
{
kind: 'text',
id: 'txt',
when: (input) => input.mode === 'text',
next: 'place',
defaultValue: '<>',
prompt: (input) => {
const value = measured(input);
return {
text: '치수 문자 입력',
...(value === null ? {} : { default: { kind: 'enter' as const, label: value } }),
};
},
},
{
kind: 'point',
id: 'place',
prompt: { text: '치수선의 위치 지정', options: [OPT.TEXT], absent: [MTEXT, ANGLE] },
options: { TEXT: { mode: 'text', next: 'txt' } },
},
],
commit: (input) => {
const entity = input.get<Entity>('obj');
const text = input.has('txt') ? input.get<string>('txt') : undefined;
const parts = radialDimension(entity, input.get<Point>('place'), diameter, text);
if (!parts) {
toast.warn('원 또는 호를 선택하십시오.');
return;
}
addEntities(parts, true);
logLine(`치수 문자 = ${labelWith(radialLabel(entity, diameter) as string, text)}`);
},
});
}
export const dimRadiusToolStateMachine = radialTool(Tool.DIMRADIUS, false);
export const dimDiameterToolStateMachine = radialTool(Tool.DIMDIAMETER, true);
export const dimAngularToolStateMachine = createSequenceTool({
tool: Tool.DIMANGULAR,
steps: [
{ kind: 'point', instructions: '각의 꼭짓점을 지정하십시오.' },
{ kind: 'point', instructions: '첫 번째 변 위의 점을 지정하십시오.' },
{ kind: 'point', instructions: '두 번째 변 위의 점을 지정하십시오.' },
{ kind: 'point', prompt: { text: '각도 정점 지정' } },
{ kind: 'point', prompt: { text: '첫 번째 각도 끝점 지정' } },
{ kind: 'point', prompt: { text: '두 번째 각도 끝점 지정' } },
],
commit: (input) => {
const [vertex, first, second] = input.points();
@@ -128,8 +170,8 @@ export const dimAngularToolStateMachine = createSequenceTool({
export const dimArcToolStateMachine = createSequenceTool({
tool: Tool.DIMARC,
steps: [
{ kind: 'entity', instructions: '호 길이를 기입할 호를 선택하십시오.' },
{ kind: 'point', instructions: '치수 문자 위치를 지정하십시오.' },
{ kind: 'entity', prompt: { text: '호 또는 폴리선 호 세그먼트 선택' } },
{ kind: 'point', prompt: { text: '호 길이 치수 위치 지정' } },
],
commit: (input) => {
const entity = input.entity(0);
@@ -146,8 +188,8 @@ export const dimArcToolStateMachine = createSequenceTool({
export const dimOrdinateToolStateMachine = createSequenceTool({
tool: Tool.DIMORDINATE,
steps: [
{ kind: 'point', instructions: '좌표를 기입할 피처 위치를 지정하십시오.' },
{ kind: 'point', instructions: '지시선 끝점을 지정하십시오.' },
{ kind: 'point', prompt: { text: '피쳐 위치 지정' } },
{ kind: 'point', prompt: { text: '지시선 끝점 지정' } },
],
commit: (input) => {
const [feature, leaderEnd] = input.points();