feat(B07): 표를 객체로 만들어 도면의 표가 진짜 표가 되게 한다
표가 DXF의 표로 나가야 한다(사용자 확정). 지금까지 도면의 표는 선과 문자 뭉치라 내보낼 때 고를 수 있는 길이 하나뿐이었다. - TableEntity: 열별 폭·행별 높이·칸 문자·병합을 한 객체가 들고 있다. 격자선은 담지 않고 병합 자리에서 선을 끊는 규칙을 표가 스스로 안다(helpers/table-geometry.ts). 회전·대칭은 지원하지 않는다 — 표는 축에 붙어 있다. - 명령: TABLE을 표 객체 생성으로 다시 쓰고 TABLEEDIT(칸 문자)·TABLEROW·TABLECOL· TABLEMERGE·TABLEUNMERGE를 더했다. EXPLODE는 표를 선과 문자로 흩는다. - 그립: 좌측 상단으로 표를 옮기고, 열·행 경계로 폭·높이를 바꾼다. - 백엔드: 유역 정보표와 횡단 수량 산출표를 표 객체로 낸다. 횡단표는 머리행이 폭 8등분, 본문이 11열 가중치로 격자가 서로 달라 두 경계를 합친 18열로 만들고 병합으로 원래 칸을 되살렸다 — 손으로 하던 가로선 끊기가 사라졌다. - 수량 역추출: 값 Text의 결정적 id로 읽던 것을 칸에 실은 key로 읽도록 옮겼다. 이미 저장된 도면을 위해 옛 방식을 폴백으로 남겼다. 토적도·종단표는 값이 칸이 아니라 측점 위치에 놓이는 성격이라 이관하지 않았다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
* 수정 명령의 객체 조작 — 간격띄우기·연장·길이조정·분해·결합·중복정리.
|
||||
* 기하 계산은 helpers/geometry의 순수 함수를 쓰고, 여기서는 엔티티로 바꾼다.
|
||||
*/
|
||||
import { Circle, Point, Segment } from '@flatten-js/core';
|
||||
import { Circle, Point, Segment, Vector } from '@flatten-js/core';
|
||||
import { ArcEntity } from '../../entities/ArcEntity';
|
||||
import { CircleEntity } from '../../entities/CircleEntity';
|
||||
import { type Entity, EntityName } from '../../entities/Entity';
|
||||
@@ -10,9 +10,12 @@ import type { HatchEntity } from '../../entities/HatchEntity';
|
||||
import { LineEntity } from '../../entities/LineEntity';
|
||||
import { PolyLineEntity } from '../../entities/PolyLineEntity';
|
||||
import type { RectangleEntity } from '../../entities/RectangleEntity';
|
||||
import type { TableEntity } from '../../entities/TableEntity';
|
||||
import { TextEntity } from '../../entities/TextEntity';
|
||||
import { dedupeConsecutive, sampleEntityPoints } from '../../helpers/geometry/sample-entity';
|
||||
import { intersectLines, offsetPolylinePoints } from '../../helpers/geometry/shape-points';
|
||||
import { polygonToSegments } from '../../helpers/polygon-to-segments';
|
||||
import { cellRect, cellTextAnchor, tableBorders } from '../../helpers/table-geometry';
|
||||
import { getActiveLayerId } from '../../state';
|
||||
|
||||
/** 원본 객체의 표시 특성을 새 객체에 옮긴다 */
|
||||
@@ -148,7 +151,11 @@ export function lengthenLine(entity: Entity, delta: number, nearPoint: Point): E
|
||||
shape.end
|
||||
);
|
||||
}
|
||||
return makeLine(entity, shape.start, new Point(shape.end.x + dx * delta, shape.end.y + dy * delta));
|
||||
return makeLine(
|
||||
entity,
|
||||
shape.start,
|
||||
new Point(shape.end.x + dx * delta, shape.end.y + dy * delta)
|
||||
);
|
||||
}
|
||||
|
||||
/** EXPLODE — 복합 객체를 구성요소로 나눈다. 나눌 게 없으면 빈 배열 */
|
||||
@@ -163,6 +170,9 @@ export function explodeEntity(entity: Entity): Entity[] {
|
||||
makeLine(entity, segment.start, segment.end)
|
||||
);
|
||||
}
|
||||
if (entity.getType() === EntityName.Table) {
|
||||
return explodeTable(entity as TableEntity);
|
||||
}
|
||||
if (entity.getType() === EntityName.Hatch) {
|
||||
const boundary = makePolyLine(entity, (entity as HatchEntity).getPoints());
|
||||
return boundary ? [boundary] : [];
|
||||
@@ -170,6 +180,36 @@ export function explodeEntity(entity: Entity): Entity[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** 표를 경계선과 칸 문자로 흩는다 (EXPLODE) */
|
||||
function explodeTable(table: TableEntity): Entity[] {
|
||||
const origin = table.getOrigin();
|
||||
const columnWidths = table.getColumnWidths();
|
||||
const rowHeights = table.getRowHeights();
|
||||
const style = table.getStyle();
|
||||
const parts: Entity[] = tableBorders(origin, columnWidths, rowHeights, table.getCells()).map(
|
||||
(border) => makeLine(table, new Point(border.x1, border.y1), new Point(border.x2, border.y2))
|
||||
);
|
||||
for (let row = 0; row < rowHeights.length; row += 1) {
|
||||
for (let column = 0; column < columnWidths.length; column += 1) {
|
||||
const cell = table.getCell(row, column);
|
||||
if (!cell?.text) continue;
|
||||
const rect = cellRect(origin, columnWidths, rowHeights, row, column, cell);
|
||||
const anchor = cellTextAnchor(rect, cell.align, style.padding);
|
||||
const text = new TextEntity(table.layerId, cell.text, new Point(anchor.x, anchor.y), {
|
||||
textDirection: new Vector(1, 0),
|
||||
textAlign: cell.align ?? 'center',
|
||||
textColor: cell.color ?? style.textColor,
|
||||
fontSize: cell.fontSize ?? style.fontSize,
|
||||
fontFamily: style.fontFamily,
|
||||
bold: cell.bold,
|
||||
italic: cell.italic,
|
||||
});
|
||||
parts.push(copyStyle(table, text));
|
||||
}
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
/** JOIN — 끝점이 맞닿는 객체들을 하나의 폴리선으로 잇는다 */
|
||||
export function joinEntities(entities: Entity[], tolerance = 1e-3): PolyLineEntity | null {
|
||||
const chains = entities
|
||||
|
||||
Reference in New Issue
Block a user