- 복제 함수 cloneKeepingLook 한 곳(move-tool.helpers) — 옮기기 · 크기 · 회전은 id · 묶음까지 그대로 · 복사 · 배열은 새 id - LineEntity · ArcEntity clone 이 색 · 층 · 선 꼴 유지(폴리선 자식 포함) - 복사가 누를 때마다 원본을 한 벌씩 더 쌓던 흠 고침 - ArcEntity mirror 가 축 각도 따름(α → 2θ − α) · 그리기 끝각 = 시작각 ± 쓸어간 각(끝각 0 → 2π 온 원 흠) - DXF 내보내기 시계방향 호는 시작 · 끝 바꿔 같은 작은 호 - 시험: ArcEntity.test.ts 10 · resources/tester/cad/clone-keeps-look.test.ts 7 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UoSr2sqvYtBf1dtQdus88s
122 lines
4.3 KiB
TypeScript
122 lines
4.3 KiB
TypeScript
import { type Arc, Point } from '@flatten-js/core';
|
|
import { describe, expect, it } from 'vitest';
|
|
import { EPSILON } from '../App.consts.ts';
|
|
import type { DrawController } from '../drawControllers/DrawController.ts';
|
|
import { mirrorPointOverAxis } from '../helpers/mirror-point-over-axis.ts';
|
|
import { ArcEntity } from './ArcEntity.ts';
|
|
import { LineEntity } from './LineEntity.ts';
|
|
|
|
describe('ArcEntity.distanceTo', () => {
|
|
/**
|
|
* ---X---
|
|
* ----- -----
|
|
* -- --
|
|
*/
|
|
it('distance to point on arc', () => {
|
|
const arc = new ArcEntity('layer1', new Point(0, 0), 1, Math.PI / 4, (3 * Math.PI) / 4, true);
|
|
const point = (arc.getShape() as Arc).pointAtLength(
|
|
(arc.getShape() as Arc).length / 2
|
|
) as Point;
|
|
const distanceInfo = arc.distanceTo(point);
|
|
expect(distanceInfo).toBeDefined();
|
|
if (!distanceInfo) return;
|
|
expect(distanceInfo[0]).to.equal(0);
|
|
});
|
|
|
|
/**
|
|
* ------- X
|
|
* ----- -----
|
|
* -- --
|
|
*/
|
|
it('distance to point outside arc', () => {
|
|
const arc = new ArcEntity('layer1', new Point(0, 0), 1, Math.PI / 4, (3 * Math.PI) / 4, true);
|
|
const point = new Point(2, 2);
|
|
const distanceInfo = arc.distanceTo(point);
|
|
expect(distanceInfo).toBeDefined();
|
|
if (!distanceInfo) return;
|
|
expect(distanceInfo[0]).to.be.closeTo(Math.sqrt(2 * 2 + 2 * 2) - 1, EPSILON);
|
|
});
|
|
|
|
/**
|
|
* -------
|
|
* ----- -----
|
|
* -- --
|
|
* - X -
|
|
*/
|
|
it('distance to point inside arc', () => {
|
|
const arc = new ArcEntity('layer1', new Point(0, 0), 1, Math.PI / 4, (3 * Math.PI) / 4, true);
|
|
const point = new Point(0.5, 0.5);
|
|
const distanceInfo = arc.distanceTo(point);
|
|
expect(distanceInfo).toBeDefined();
|
|
if (!distanceInfo) return;
|
|
expect(distanceInfo[0]).to.be.closeTo(1 - Math.sqrt(0.5 * 0.5 + 0.5 * 0.5), EPSILON);
|
|
});
|
|
|
|
it('should return distance from a point to an arc', () => {
|
|
const arc = new ArcEntity('layer1', new Point(20, 20), 20, 0, 2 * Math.PI * 0.75, true);
|
|
const distanceInfo = arc.distanceTo(new Point(20 - 14.14, 20 + 14.14));
|
|
expect(distanceInfo).toBeDefined();
|
|
if (!distanceInfo) return;
|
|
expect(distanceInfo[0]).toBeLessThan(1);
|
|
});
|
|
});
|
|
|
|
describe('ArcEntity 대칭 · 그리기 · 복제(84-3)', () => {
|
|
const middle = (entity: ArcEntity) => {
|
|
const arc = entity.getShape() as Arc;
|
|
return arc.pointAtLength(arc.length / 2) as Point;
|
|
};
|
|
const mirrorOver = (axisEnd: Point) => {
|
|
const arc = new ArcEntity('layer1', new Point(2, 1), 1, 0, Math.PI / 2, true);
|
|
const before = arc.getShape() as Arc;
|
|
const axis = new LineEntity('layer1', new Point(0, 0), axisEnd);
|
|
const expected = [before.start, middle(arc), before.end].map((point) =>
|
|
mirrorPointOverAxis(point, axis)
|
|
);
|
|
arc.mirror(axis);
|
|
const after = arc.getShape() as Arc;
|
|
return { expected, actual: [after.start, middle(arc), after.end], length: after.length };
|
|
};
|
|
|
|
for (const [name, axisEnd] of [
|
|
['세로축', new Point(0, 1)],
|
|
['가로축', new Point(1, 0)],
|
|
['45° 축', new Point(1, 1)],
|
|
['30° 축', new Point(Math.cos(Math.PI / 6), Math.sin(Math.PI / 6))],
|
|
] as const) {
|
|
it(`${name} 대칭 — 끝점 · 가운데 점이 대칭 자리 · 작은 호 그대로`, () => {
|
|
const { expected, actual, length } = mirrorOver(axisEnd);
|
|
for (let index = 0; index < 3; index++) {
|
|
expect(actual[index].x).toBeCloseTo(expected[index].x, 9);
|
|
expect(actual[index].y).toBeCloseTo(expected[index].y, 9);
|
|
}
|
|
expect(length).toBeCloseTo(Math.PI / 2, 9);
|
|
});
|
|
}
|
|
|
|
it('시작각이 음수 · 끝각 0 인 호 — 끝각을 2π 로 바꾸지 않음(온 원으로 안 그림)', () => {
|
|
const arc = new ArcEntity('layer1', new Point(0, 0), 1, -Math.PI / 2, 0, true);
|
|
const calls: number[][] = [];
|
|
const controller = {
|
|
setLineStyles: () => {},
|
|
drawArc: (_c: Point, _r: number, start: number, end: number) => calls.push([start, end]),
|
|
} as unknown as DrawController;
|
|
arc.draw(controller);
|
|
expect(calls[0][1] - calls[0][0]).toBeCloseTo(Math.PI / 2, 9);
|
|
});
|
|
|
|
it('복제 — 색 · 층 · 선 꼴 그대로', () => {
|
|
const arc = new ArcEntity('구조물', new Point(0, 0), 1, 0, 1, false);
|
|
arc.lineColor = '#ff0000';
|
|
arc.lineWidth = 3;
|
|
arc.lineDash = [2, 1];
|
|
const copy = arc.clone();
|
|
expect([copy.layerId, copy.lineColor, copy.lineWidth, copy.lineDash]).toEqual([
|
|
'구조물',
|
|
'#ff0000',
|
|
3,
|
|
[2, 1],
|
|
]);
|
|
});
|
|
});
|