@g1cloud/entity-modeler-next 5.3.0 → 5.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/autolayout.d.ts +110 -1
- package/dist/entity-modeler.js +10031 -9821
- package/dist/entity-modeler.umd.cjs +27 -27
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DiagramLayout, LogicalModel, ModelId, Point } from './types';
|
|
1
|
+
import { DiagramLayout, Entity, Geo, LogicalModel, ModelId, Point, Size } from './types';
|
|
2
2
|
export interface AutoLayoutGrid {
|
|
3
3
|
cellWidth: number;
|
|
4
4
|
cellHeight: number;
|
|
@@ -54,6 +54,21 @@ export interface PackBoxesOptions {
|
|
|
54
54
|
}
|
|
55
55
|
/** 속성 수 → 박스 추정 높이 (넉넉히 over-reserve). */
|
|
56
56
|
export declare function estimateEntityHeight(attributeCount: number, opts?: Partial<PackBoxesOptions>): number;
|
|
57
|
+
/**
|
|
58
|
+
* 엔티티 박스 크기 추정 — 배치 계산용(저장하지 않는다). 렌더 크기는 max(저장 크기, 콘텐츠)라
|
|
59
|
+
* 저장 크기(폭 200 고정 · 추가 시점 높이)만 보고 배치하면 실제 박스가 이웃을 덮는다.
|
|
60
|
+
*
|
|
61
|
+
* ★실측 근거(GUI 프로브 `verify-agent-layered-layout`): 이름이 짧아도 렌더 폭 202~259, 물리명 36자 한 행이면
|
|
62
|
+
* **392**(이웃과 132px 겹침). 폭은 «가장 긴 행»이 정한다 — 행 = 좌우 패딩 10 + 키 슬롯 26 + 간격 6×2 +
|
|
63
|
+
* 라벨(12px) + 타입(11px)이다. 실측 글자 폭을 넣으면 이 산식이 392 를 391 로 재현한다 — 아래 상수는 그보다
|
|
64
|
+
* 넉넉하다(긴 행에서 약 20% 과대).
|
|
65
|
+
*
|
|
66
|
+
* 표시 모드는 **가장 넓은 논리·물리 모드**로 본다(과대 추정) — 에이전트가 추가한 엔티티는 표시 모드가 없어
|
|
67
|
+
* 기본값이 그 모드이고, 다른 모드는 라벨이 더 짧다. 접힘·임베드 일괄 접기도 «펼침»으로 본다.
|
|
68
|
+
* 높이는 속성 수가 아니라 **렌더 행 수**(임베드·다중 컬럼 FK 의 서브컬럼 행 포함)로 `estimateEntityHeight` 에 넘긴다.
|
|
69
|
+
* 비어 있는 물리명(상속·FK 파생)은 필드명 snake_case 로 근사한다.
|
|
70
|
+
*/
|
|
71
|
+
export declare function estimateEntitySize(entity: Pick<Entity, 'attributes' | 'classLogicalName' | 'table'>, opts?: Partial<PackBoxesOptions>): Size;
|
|
57
72
|
export declare function packEntityBoxes(boxes: EntityBoxSpec[], options?: Partial<PackBoxesOptions>): PackedEntityBox[];
|
|
58
73
|
/**
|
|
59
74
|
* 생성 지점 카스케이드 — **이미 점유된 자리에 새 요소를 포개 놓지 않는다.**
|
|
@@ -76,3 +91,97 @@ export declare function cascadeFreeLocation(desired: Point, occupied: readonly P
|
|
|
76
91
|
step?: number;
|
|
77
92
|
maxSteps?: number;
|
|
78
93
|
}): Point;
|
|
94
|
+
/** 한 엔티티의 새 자리. */
|
|
95
|
+
export interface BoxPlacement {
|
|
96
|
+
id: ModelId;
|
|
97
|
+
location: Point;
|
|
98
|
+
}
|
|
99
|
+
export interface DropCanvas {
|
|
100
|
+
/** 엔티티 박스 — 행을 이루고, 겹침 회피 대상이다. */
|
|
101
|
+
entities: readonly Geo[];
|
|
102
|
+
/** 그룹 박스·노트 — 겹침 회피만 한다(행을 정하지 않는다). */
|
|
103
|
+
obstacles: readonly Geo[];
|
|
104
|
+
}
|
|
105
|
+
export interface DropRowOptions {
|
|
106
|
+
/** 격자 열 수 — 행이 이 범위를 넘으면 새 행. */
|
|
107
|
+
cols: number;
|
|
108
|
+
/** 격자 칸 폭(열 간격 = boxWidth + gap). */
|
|
109
|
+
boxWidth: number;
|
|
110
|
+
/** 칸 사이 여백 및 겹침 판정 마진. */
|
|
111
|
+
gap: number;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 에이전트가 추가한 엔티티를 «마지막 행 오른쪽에 이어» 떨군다 — 입력 순서대로 자리를 고른다.
|
|
115
|
+
*
|
|
116
|
+
* ★배경: 스킬은 대량 추가를 실패 격리 때문에 «엔티티당 1 POST»로 보낸다(syncPlaybook 대량 add 실행 순서).
|
|
117
|
+
* `packEntityBoxes` 는 열을 `i % cols`(배치 **안** 순번)로 골라서, POST 마다 i=0 → x=0 열 · 전체 최하단
|
|
118
|
+
* 아래로 떨어져 **세로 일렬**이 됐다. 5열 설계가 호출 패턴 때문에 1열로 줄어든 것이다.
|
|
119
|
+
*
|
|
120
|
+
* ★배치 분할 불변 — 배치 경계를 기억하지 않고 «현재 캔버스»만 보고, 배치 안에서 앞서 놓은 박스도 다음
|
|
121
|
+
* 판정에 누적한다. 그래서 6개를 한 배치로 보내든 1개씩 6번 보내든 같은 자리에 놓인다.
|
|
122
|
+
*
|
|
123
|
+
* ★«오른쪽으로만» 잇는다 — 사람이 배치한 다이어그램의 마지막 행 왼쪽 빈 칸은 의도된 여백이거나 관계선
|
|
124
|
+
* 통로일 수 있다. 거기 끼우면 무관한 엔티티가 기존 군집에 섞여 보인다. 오른쪽 끝 다음 칸이 격자 범위를
|
|
125
|
+
* 넘으면 잇지 않고 새 행으로 간다.
|
|
126
|
+
*
|
|
127
|
+
* 관계는 보지 않는다 — 연결 시점 재배치는 `computeLayeredPlacements` 소관. 크기는 호출자가 추정해 넘긴다
|
|
128
|
+
* (`estimateEntitySize` — 캔버스의 기존 박스도 max(저장, 추정)으로 넘겨야 렌더 크기를 덮는다).
|
|
129
|
+
*/
|
|
130
|
+
export declare function computeDropRowPlacements(specs: ReadonlyArray<{
|
|
131
|
+
id: ModelId;
|
|
132
|
+
size: Size;
|
|
133
|
+
}>, canvas: DropCanvas, options?: Partial<DropRowOptions>): BoxPlacement[];
|
|
134
|
+
/** 계층 간선 — `parent` 가 위층, `child` 가 아래층. ER 관계면 end1(부모·"1"측) → end2(자식·FK 측). */
|
|
135
|
+
export interface LayeredEdge {
|
|
136
|
+
parent: ModelId;
|
|
137
|
+
child: ModelId;
|
|
138
|
+
}
|
|
139
|
+
export interface LayeredPlacementInput {
|
|
140
|
+
/**
|
|
141
|
+
* 캔버스의 엔티티 박스 전부(옮길 것·고정될 것 모두). 여기 없는 id 의 간선은 배치에 참여하지 않는다.
|
|
142
|
+
* 크기는 max(저장, `estimateEntitySize`) — 관계가 붙으며 자식에 FK 행이 늘어 저장 크기보다 커진다.
|
|
143
|
+
*/
|
|
144
|
+
entities: ReadonlyArray<{
|
|
145
|
+
id: ModelId;
|
|
146
|
+
geo: Geo;
|
|
147
|
+
}>;
|
|
148
|
+
/** 그룹 박스·노트 — 피하기만 한다. */
|
|
149
|
+
obstacles: readonly Geo[];
|
|
150
|
+
/** 이번에 생긴 관계. 순서가 결정성의 기준이다(덩어리 순서·층 안 초기 순서·동점 처리). */
|
|
151
|
+
edges: readonly LayeredEdge[];
|
|
152
|
+
/** 옮겨도 되는 엔티티. 나머지는 고정이고, 간선으로 닿으면 앵커가 된다. 게이트는 호출자 정책이다. */
|
|
153
|
+
movable: ReadonlySet<ModelId>;
|
|
154
|
+
}
|
|
155
|
+
export interface LayeredPlacementOptions extends DropRowOptions {
|
|
156
|
+
/** 층 사이 세로 간격(관계선·다중성 마커가 지나갈 공간). */
|
|
157
|
+
layerGap: number;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* 이번에 연결된 엔티티 덩어리를 통째로 **위→아래 계층 배치**한다 — 부모를 위에, 자식을 아래 한 행에 가로로.
|
|
161
|
+
*
|
|
162
|
+
* ★왜 덩어리 단위인가: 종전(A2 snapOnRelate)은 노드 하나씩 부모 오른쪽 빈 칸에 탐욕 배치해서, 루트·무관계
|
|
163
|
+
* 엔티티는 제자리에 남고 형제는 부모 오른쪽에 다시 세로로 쌓였다. 어느 시점도 «새로 들어온 덩어리 전체»를
|
|
164
|
+
* 보지 못한 것이 근본이다(헤더의 두 시점 참조).
|
|
165
|
+
* ★왜 위→아래인가: 박스가 속성 수만큼 세로로 길어 좌→우 계층이면 형제가 다시 세로로 쌓인다.
|
|
166
|
+
* ★왜 자체 구현인가: dagre 등은 lib 런타임 의존성을 소비자까지 늘린다. ER 모델은 다대일 위주 비순환이라
|
|
167
|
+
* longest-path + barycenter 로 충분하고, 결정적이라 테스트로 고정된다.
|
|
168
|
+
*
|
|
169
|
+
* 절차
|
|
170
|
+
* 1. 옮길 노드 = 간선 끝점 ∩ `movable` ∩ 위치 있음. 옮길 노드끼리의 간선으로 **덩어리**를 묶는다.
|
|
171
|
+
* 2. 덩어리에 간선으로 닿는 고정 엔티티 중 **부모**(고정 → 덩어리)가 앵커 후보. 덩어리로 들어오는 간선이
|
|
172
|
+
* 가장 많은 것을 고르고 동점은 입력 순서다. 고정 **자식**만 있으면 앵커 없음으로 본다 — 위→아래면 블록이
|
|
173
|
+
* 앵커 위로 가야 하는데 그 공간은 대개 기존 내용이 차지한다.
|
|
174
|
+
* 3. 덩어리를 블록으로 층 배치한다(`layerBlock`). **같은 앵커를 고른 덩어리들은 가로로 이어 한 블록**으로
|
|
175
|
+
* 만든다 — 고정 부모 아래 새 형제들은 서로 간선이 없어 각자 단일 노드 덩어리인데, 따로 놓으면 하나씩
|
|
176
|
+
* 빈 칸을 찾다가 다시 세로로 쌓인다(종전 증상의 재현).
|
|
177
|
+
* 4. 블록 자리: 앵커가 있으면 앵커의 자식들 중심이 앵커 중심 아래에 오도록 `layerGap` 아래에서 시작해
|
|
178
|
+
* 가장 가까운 빈 자리(`findBelow`). 앵커가 없으면 블록을 박스 하나로 보고 행 이어 채우기 규칙.
|
|
179
|
+
* 블록은 **바운딩 박스 통째로** 점유한다 — 뒤 블록이 앞 블록의 빈 틈에 끼면 두 덩어리가 섞여 보인다.
|
|
180
|
+
*
|
|
181
|
+
* ⚠️앵커 없는 **단일 노드** 덩어리는 옮기지 않는다 — 추가 시점에 이미 행 이어 채우기 자리에 있다.
|
|
182
|
+
* ⚠️사람 배치 침범 금지는 `movable` 게이트(호출자) 소관이다. 고정 엔티티는 앵커·장애물로만 쓴다.
|
|
183
|
+
* ⚠️관계가 여러 배치로 나뉘어 오면 앞 배치에서 관계를 얻은 엔티티는 호출자 게이트상 고정이 되어, 뒤
|
|
184
|
+
* 배치는 그것을 앵커로 삼는 배치로 자연히 물러선다(덩어리 일부만 정렬 — 수용).
|
|
185
|
+
* ⚠️자리가 그대로인 노드는 결과에서 뺀다(불필요한 이동 op 방지). 결과 순서 = 배치 단위 순서 → 층 순서.
|
|
186
|
+
*/
|
|
187
|
+
export declare function computeLayeredPlacements(input: LayeredPlacementInput, options?: Partial<LayeredPlacementOptions>): BoxPlacement[];
|