@operato/scene-storage 10.0.0-beta.40 → 10.0.0-beta.42

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.
Files changed (102) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/MIGRATION-plan-a-slot-api.md +266 -0
  3. package/PLAN-A-rack-as-slot-holder.md +164 -0
  4. package/dist/box.js +18 -0
  5. package/dist/box.js.map +1 -1
  6. package/dist/crane-3d.d.ts +47 -2
  7. package/dist/crane-3d.js +246 -89
  8. package/dist/crane-3d.js.map +1 -1
  9. package/dist/crane.d.ts +96 -12
  10. package/dist/crane.js +395 -100
  11. package/dist/crane.js.map +1 -1
  12. package/dist/index.d.ts +3 -4
  13. package/dist/index.js +1 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/pallet.d.ts +15 -0
  16. package/dist/pallet.js +38 -2
  17. package/dist/pallet.js.map +1 -1
  18. package/dist/parcel-3d.js +22 -18
  19. package/dist/parcel-3d.js.map +1 -1
  20. package/dist/parcel.d.ts +4 -3
  21. package/dist/parcel.js +24 -5
  22. package/dist/parcel.js.map +1 -1
  23. package/dist/rack-grid-3d.d.ts +18 -7
  24. package/dist/rack-grid-3d.js +372 -69
  25. package/dist/rack-grid-3d.js.map +1 -1
  26. package/dist/rack-grid-cell.d.ts +21 -72
  27. package/dist/rack-grid-cell.js +147 -243
  28. package/dist/rack-grid-cell.js.map +1 -1
  29. package/dist/rack-grid.d.ts +277 -56
  30. package/dist/rack-grid.js +1230 -695
  31. package/dist/rack-grid.js.map +1 -1
  32. package/dist/rack-materials.d.ts +9 -0
  33. package/dist/rack-materials.js +55 -0
  34. package/dist/rack-materials.js.map +1 -0
  35. package/dist/storage-rack-3d.d.ts +15 -0
  36. package/dist/storage-rack-3d.js +165 -29
  37. package/dist/storage-rack-3d.js.map +1 -1
  38. package/dist/storage-rack.d.ts +253 -32
  39. package/dist/storage-rack.js +726 -66
  40. package/dist/storage-rack.js.map +1 -1
  41. package/package.json +3 -3
  42. package/src/box.ts +18 -0
  43. package/src/crane-3d.ts +258 -93
  44. package/src/crane.ts +445 -110
  45. package/src/index.ts +3 -4
  46. package/src/pallet.ts +50 -1
  47. package/src/parcel-3d.ts +23 -18
  48. package/src/parcel.ts +24 -5
  49. package/src/rack-grid-3d.ts +383 -80
  50. package/src/rack-grid-cell.ts +161 -305
  51. package/src/rack-grid.ts +1263 -762
  52. package/src/rack-materials.ts +61 -0
  53. package/src/storage-rack-3d.ts +182 -29
  54. package/src/storage-rack.ts +819 -67
  55. package/test/test-carrier-lifecycle.ts +361 -0
  56. package/test/test-coord-alignment.ts +201 -0
  57. package/test/test-crane-geometry.ts +167 -0
  58. package/test/test-external-to-rack.ts +461 -0
  59. package/test/test-mover-concurrent-bug.ts +304 -0
  60. package/test/test-mover-rollback.ts +290 -0
  61. package/test/test-phase-h-carrier-pickable.ts +4 -3
  62. package/test/test-r19-place-absorb.ts +174 -0
  63. package/test/test-rack-3d-attach-real.ts +301 -0
  64. package/test/test-rack-concurrent.ts +254 -0
  65. package/test/test-rack-edge-cases.ts +323 -0
  66. package/test/test-rack-grid-cell.ts +318 -0
  67. package/test/test-rack-grid-location.ts +657 -0
  68. package/test/test-real-3d-positioning.ts +158 -0
  69. package/test/test-slot-center-convention.ts +116 -0
  70. package/test/test-slot-target.ts +189 -0
  71. package/test/test-storage-rack-batched.ts +606 -0
  72. package/test/test-storage-rack-click.ts +329 -0
  73. package/test/test-storage-rack-slot-api.ts +357 -0
  74. package/test/test-toscene-convention.ts +162 -0
  75. package/test/test-user-scenario-sequential.ts +334 -0
  76. package/translations/en.json +7 -1
  77. package/translations/ja.json +7 -1
  78. package/translations/ko.json +7 -1
  79. package/translations/ms.json +7 -1
  80. package/translations/zh.json +7 -1
  81. package/tsconfig.tsbuildinfo +1 -1
  82. package/dist/rack-column.d.ts +0 -35
  83. package/dist/rack-column.js +0 -258
  84. package/dist/rack-column.js.map +0 -1
  85. package/dist/rack-grid-helpers.d.ts +0 -28
  86. package/dist/rack-grid-helpers.js +0 -71
  87. package/dist/rack-grid-helpers.js.map +0 -1
  88. package/dist/rack-grid-location.d.ts +0 -37
  89. package/dist/rack-grid-location.js +0 -227
  90. package/dist/rack-grid-location.js.map +0 -1
  91. package/dist/storage-cell-3d.d.ts +0 -25
  92. package/dist/storage-cell-3d.js +0 -88
  93. package/dist/storage-cell-3d.js.map +0 -1
  94. package/dist/storage-cell.d.ts +0 -70
  95. package/dist/storage-cell.js +0 -197
  96. package/dist/storage-cell.js.map +0 -1
  97. package/src/rack-column.ts +0 -340
  98. package/src/rack-grid-helpers.ts +0 -77
  99. package/src/rack-grid-location.ts +0 -286
  100. package/src/storage-cell-3d.ts +0 -101
  101. package/src/storage-cell.ts +0 -247
  102. package/test/test-rack-grid.ts +0 -77
@@ -1,80 +1,301 @@
1
- import { Component, ComponentNature, ContainerAbstract, Control, Properties, RealObject } from '@hatiolab/things-scene';
1
+ import { Component, ComponentNature, Control, Properties, RealObject } from '@hatiolab/things-scene';
2
2
  import type { State } from '@hatiolab/things-scene';
3
3
  import * as THREE from 'three';
4
- /** RackGrid 컴포넌트 state */
4
+ import { SlotTarget, type Alignment, type Heights, type PlacementArchetype, type SlotRecord, type SlottedHolder } from '@operato/scene-base';
5
+ import RackGridCell from './rack-grid-cell.js';
6
+ /** 한 (col, row) 위치의 메타데이터. 명시되지 않은 위치는 *룰의 기본값* 적용 (= location 없음). */
7
+ export interface CellOverride {
8
+ /** Location 의 `{s}` 부분. unit 과 함께 명시되어야 location 부여됨. */
9
+ section?: string;
10
+ /** Location 의 `{u}` 부분. section 과 함께 명시되어야 location 부여됨. */
11
+ unit?: string;
12
+ /**
13
+ * 명시적 통로/공실 표시. 의미:
14
+ * - location 미부여 (이 cell 로는 외부 location key 매칭 불가)
15
+ * - increaseLocation 의 aisle row 로 인식 (skipNumbering 시 건너뜀)
16
+ * 자식 컴포넌트 종류는 무관 — Crane/AGV/forklift/비움 모두 OK.
17
+ */
18
+ isEmpty?: boolean;
19
+ /** 4-side border style (top/left/bottom/right) — modeling 시각 용. */
20
+ border?: any;
21
+ /** Grid merge — 시각 표현. */
22
+ merged?: boolean;
23
+ rowspan?: number;
24
+ colspan?: number;
25
+ }
5
26
  export interface RackGridState extends State {
6
- rows?: number;
27
+ /** Grid 가로 칸 수 (X 축). 각 칸 ≈ 1 column 의 StorageRack (또는 Crane 등). */
7
28
  columns?: number;
8
- zone?: string;
29
+ /** Grid 세로 칸 수 (Z 축, depth). 통상 1, double-deep rack 등은 2. */
30
+ rows?: number;
31
+ /** 각 (col, row) 의 수직 shelf 수 (Y 축, level). 자식 StorageRack 의 levels 와 일치. */
9
32
  shelves?: number;
33
+ /**
34
+ * 첫 shelf 의 시작 높이 (mm, 3D Y 축, 바닥부터). 미명시 0 (바닥 = 첫 shelf).
35
+ * 양수 시 그만큼 위로 올라가 *stocker port / conveyor 같은 컴포넌트가 들어갈 빈 공간*
36
+ * 확보. Frame uprights 는 바닥 ~ 천장 그대로.
37
+ */
38
+ shelfBaseHeight?: number;
39
+ zone?: string;
40
+ /** 기본 `'{z}{s}-{u}-{sh}'`. placeholders: {z} {s} {u} {sh}. */
10
41
  locPattern?: string;
42
+ /** Section 숫자 자리수 (zero-pad). 기본 2. */
11
43
  sectionDigits?: number;
44
+ /** Unit 숫자 자리수 (zero-pad). 기본 2. */
12
45
  unitDigits?: number;
46
+ /** Shelf label CSV. 예: '1,2,3' / ',,,04'. 빈 자리는 1-based 인덱스 default. */
13
47
  shelfLocations?: string;
14
- stockScale?: number;
48
+ /** Sparse cell metadata. key = posKey = `${col-1}-${row-1}` (0-based, 2 segments). */
49
+ cellOverrides?: {
50
+ [posKey: string]: CellOverride;
51
+ };
52
+ /**
53
+ * Stock records — `{ cellId: 'col-row-shelf', type, ... }`. cellId 가 RackGrid 의
54
+ * cell 위치 (3 segments) 와 매칭되는 record 만 InstancedMesh instance 로 렌더링.
55
+ * Plan A 정신 — sparse 데이터, 자식 컴포넌트 없이 batched 시각화.
56
+ */
57
+ data?: Array<{
58
+ cellId: string;
59
+ [key: string]: any;
60
+ }>;
61
+ /**
62
+ * Popup 컴포넌트 id — 클릭 시 그 popup 을 invoke (anchor = 클릭된 cell 의 SlotTarget).
63
+ * 미명시 시 click event (`rack-grid-cell-click`) 만 emit.
64
+ */
65
+ popupRef?: string;
15
66
  hideRackFrame?: boolean;
67
+ hideHorizontalFrame?: boolean;
16
68
  legendTarget?: string;
17
69
  hideEmptyStock?: boolean;
18
70
  widths?: number[];
19
71
  heights?: number[];
20
72
  }
21
- import { RackGridCell } from './rack-grid-cell.js';
22
- import type { StockMaterialProvider } from './stock.js';
23
- export default class RackGrid extends ContainerAbstract implements StockMaterialProvider {
24
- get state(): RackGridState;
25
- _focused_cell?: RackGridCell;
26
- _stock_materials: THREE.Material[];
27
- _default_material?: THREE.Material;
28
- _empty_material?: THREE.Material;
73
+ declare const RackGrid_base: any;
74
+ export default class RackGrid extends RackGrid_base implements SlottedHolder {
75
+ state: RackGridState;
76
+ static placement: PlacementArchetype;
77
+ static align: Alignment;
78
+ static defaultDepth: (h: Heights) => number;
79
+ get nature(): ComponentNature;
80
+ get anchors(): never[];
81
+ /**
82
+ * Serialize 시 자식 RackGridCell 의 redundant 속성 제거:
83
+ * - 좌표/변환 (left/top/width/height/zPos/rotation/scale/translate) — table layout 이
84
+ * runtime 에 자동 결정. state 저장하면 layout 결과와 불일치 위험.
85
+ * - **refid** — *동적 생성* 되는 자식이라 *부모의 refid 가 그대로 들어가면 충돌*
86
+ * ("Refid Index replaced" 경고). 자식의 refid 는 *load 시 새로 자동 부여*. (외부
87
+ * 참조용 *id* 는 사용자 명시 시만 유지)
88
+ *
89
+ * 유지: cellId, section, unit, isEmpty, border, shelfLocations 등 *own 데이터*.
90
+ */
91
+ get hierarchy(): any;
92
+ /** Focusible — children (RackGridCell) 도 editor 의 selection 가능 (rack-table 동일). */
93
+ get focusible(): boolean;
94
+ /** Lifecycle — children 자동 생성. columns × rows 만큼 RackGridCell 자식. */
95
+ created(): void;
96
+ /** columns/rows 변경 시 children 재구성. rack-table.buildCells 패턴. */
97
+ onchange(after: Properties, _before: Properties): void;
98
+ /** state.data 변경 시 호출 (things-scene 의 onchange<PropName>). */
99
+ onchangeData(): void;
100
+ /** state.data 의 records — Plan A 의 stock 보관소. */
101
+ get records(): Array<{
102
+ cellId: string;
103
+ [key: string]: any;
104
+ }>;
29
105
  private _legendTarget?;
106
+ /**
107
+ * Legend 컴포넌트 lookup. 우선순위:
108
+ * 1) state.legendTarget id 명시
109
+ * 2) scene 전체 의 type='legend' 첫 번째 컴포넌트 (자동 발견)
110
+ */
30
111
  get legendTarget(): Component | undefined;
31
- get hideEmptyStock(): boolean;
32
112
  private _onLegendChanged;
33
- private _resetMaterials;
34
- buildRealObject(): RealObject | undefined;
35
- dispose(): void;
36
- created(): void;
37
- get focusible(): boolean;
38
- get widths(): number[];
39
- get heights(): number[];
40
- buildCells(newrows: number, newcolumns: number, oldrows: number, oldcolumns: number): void;
41
- get layout(): any;
42
- get rows(): any;
43
- setCellsStyle(cells: RackGridCell[], style: any, where: string): void;
44
- getRowColumn(cell: RackGridCell): {
45
- column: number;
46
- row: number;
47
- };
48
- getCellsByRow(row: number): Component[];
49
- getCellsByColumn(column: number): Component[];
50
- deleteRows(cells: RackGridCell[]): void;
51
- deleteColumns(cells: RackGridCell[]): void;
52
- insertCellsAbove(cells: RackGridCell[]): false | undefined;
53
- insertCellsBelow(cells: RackGridCell[]): false | undefined;
54
- insertCellsLeft(cells: RackGridCell[]): false | undefined;
55
- insertCellsRight(cells: RackGridCell[]): false | undefined;
56
- distributeHorizontal(cells: RackGridCell[]): void;
57
- distributeVertical(cells: RackGridCell[]): void;
58
- increaseLocation(type: string, skipNumbering: boolean, startSection: number, startUnit: number): void;
59
- get columns(): any;
60
- get lefts(): Component[];
61
- get centers(): Component[];
62
- get rights(): Component[];
63
- get tops(): Component[];
64
- get middles(): Component[];
65
- get bottoms(): Component[];
66
- get all(): Component[];
67
- get widths_sum(): any;
68
- get heights_sum(): any;
69
- get nature(): ComponentNature;
70
- get controls(): Array<Control> | undefined;
71
- onchange(after: Properties, before: Properties): void;
113
+ /**
114
+ * record legend.field 값을 ranges 와 매칭해 색상 해석.
115
+ * - `range.value === recordValue` (카테고리 일치)
116
+ * - `range.min ≤ Number(v) < range.max` (수치 범위)
117
+ * - 매칭 없으면 `defaultColor` 또는 undefined
118
+ */
72
119
  get eventMap(): {
73
120
  '(self)': {
74
- '(descendant)': {
75
- change: (after: Properties, before: Properties) => void;
121
+ '(self)': {
122
+ click: (mouseEvent: MouseEvent) => void;
76
123
  };
77
124
  };
78
125
  };
79
- oncellchanged(after: Properties, before: Properties): void;
126
+ private _onViewClick;
127
+ /** state.popupRef Popup 컴포넌트 invoke. anchor = 클릭된 cell 의 SlotTarget. */
128
+ private _invokePopup;
129
+ /** raycast → 우리 RackGrid 의 어떤 mesh 가 closest hit 인지. */
130
+ private _raycastHit;
131
+ /** world point → cellId (col-row-shelf) 역산. */
132
+ private _cellIdFromWorldPoint;
133
+ resolveLegendColor(record: any): string | undefined;
134
+ /**
135
+ * 새 (rows × columns) 에 맞춰 children 재구성. rack-table.buildCells 정확 클론.
136
+ */
137
+ private _buildCells;
138
+ /** children 의 state.cellId 가 *순서 기반 bayKey* 와 일치하도록 동기화. */
139
+ private _syncChildCellIds;
140
+ /** Row 인덱스의 cell 들. rack-table-cell 의 rowCells 가 사용. */
141
+ getCellsByRow(row: number): Component[];
142
+ /** Column 인덱스의 cell 들. */
143
+ getCellsByColumn(column: number): Component[];
144
+ /** (col, row) 위치의 cell-component. */
145
+ cellAt(col: number, row?: number): RackGridCell | null;
146
+ /** bayKey 의 cell-component. */
147
+ cellAtBayKey(bayKey: string): RackGridCell | null;
148
+ /**
149
+ * (col, row) bay 가 *isEmpty* 인가. cell-component 의 state.isEmpty 가 source of
150
+ * truth, 없으면 cellOverrides[posKey].isEmpty fallback.
151
+ */
152
+ isBayEmpty(col: number, row?: number): boolean;
153
+ buildRealObject(): RealObject | undefined;
154
+ get layout(): any;
155
+ get widths(): number[];
156
+ get heights(): number[];
157
+ get widths_sum(): number;
158
+ get heights_sum(): number;
159
+ /** Column / Row 사이 드래그 핸들 — editor 의 widths/heights 가변 조절. */
160
+ get controls(): Array<Control> | undefined;
161
+ render(ctx: CanvasRenderingContext2D): void;
162
+ get columns(): number;
163
+ get rackRows(): number;
164
+ get shelves(): number;
165
+ /** posKey = `${col-1}-${row-1}` (0-based, 2 segments). 1-based input. */
166
+ posKeyOf(col: number, row?: number): string;
167
+ /** cellId = `${col-1}-${row-1}-${shelf-1}` (0-based, 3 segments). 1-based input. */
168
+ cellIdOf(col: number, row?: number, shelf?: number): string;
169
+ parseCellId(cellId: string): {
170
+ col: number;
171
+ row: number;
172
+ shelf: number;
173
+ } | null;
174
+ parsePosKey(posKey: string): {
175
+ col: number;
176
+ row: number;
177
+ } | null;
178
+ get cellOverrides(): {
179
+ [posKey: string]: CellOverride;
180
+ };
181
+ getCellOverride(posKey: string): CellOverride | undefined;
182
+ /**
183
+ * Parse `state.shelfLocations` 를 levels 길이 array 로. 빈 슬롯은 1-based index default.
184
+ * '1,2,3' → ['1', '2', '3', ...]
185
+ * ',,,04' → ['1', '2', '3', '04']
186
+ * undefined → ['1', '2', '3', '4', ...]
187
+ */
188
+ get shelfLabels(): string[];
189
+ /**
190
+ * cellId → location string. 우선순위:
191
+ * 1. cell-component (state.section/unit) — source of truth
192
+ * 2. cellOverrides[posKey] — 호환성 fallback
193
+ * section/unit 둘 다 있고 isEmpty=false 일 때만 부여. 미부여 시 null.
194
+ */
195
+ locationOf(cellId: string): string | null;
196
+ /** cell.state.shelfLocations 명시 시 그것, 아니면 RackGrid 의 shelfLabels. */
197
+ private _shelfLabelsFromCell;
198
+ /** location string → cellId. sparse 역인덱스 lookup. 없으면 null. */
199
+ cellIdOfLocation(location: string): string | null;
200
+ /** 모든 (override 명시된) cell 의 location 목록. inspection 용. */
201
+ get allLocations(): Array<{
202
+ cellId: string;
203
+ location: string;
204
+ }>;
205
+ private _locationIndexCache?;
206
+ private get _locationIndex();
207
+ /** 룰 / cellOverrides 변경 시 호출. things-scene 의 onchange* 가 자동 호출. */
208
+ invalidateLocationIndex(): void;
209
+ onchangeCellOverrides(): void;
210
+ onchangeLocPattern(): void;
211
+ onchangeShelfLocations(): void;
212
+ onchangeZone(): void;
213
+ onchangeSectionDigits(): void;
214
+ onchangeUnitDigits(): void;
215
+ onchangeShelves(): void;
216
+ /**
217
+ * (col, row) 위치의 자식 StorageRack. 자식이 StorageRack 이 아닌 경우 (Crane, AGV 등)
218
+ * 는 null — Carrier 보관은 StorageRack 만.
219
+ *
220
+ * 자식의 grid 위치 식별: 자식의 state.column / state.row (1-based) 명시. RackGrid 가
221
+ * 자식 추가 시 자동 할당 또는 사용자가 명시.
222
+ */
223
+ private _childRackAt;
224
+ hasCarrierAt(slotId: string): boolean;
225
+ obtainCarrier(slotIdOrLocation: string): Component | null;
226
+ canReceiveAt(slotIdOrLocation: string, carrier?: Component): boolean;
227
+ receiveAt(slotIdOrLocation: string, carrier: Component, options?: any): Promise<void>;
228
+ recordFromCarrier(carrier: Component, slotId: string): SlotRecord;
229
+ /**
230
+ * Slot 의 attach object3d — Stock InstancedMesh 의 instance 와 *같은 world 위치* 에
231
+ * 위치한 invisible Object3D. popup tether / Carriable.applyHolderAttachPoint 가 이
232
+ * object3d 의 matrixWorld 를 사용. lazy 생성 + cache.
233
+ */
234
+ private _attachAnchorByCell;
235
+ getSlotAttachObject3d(slotId: string): THREE.Object3D | undefined;
236
+ getSlotSize(slotId: string): {
237
+ width: number;
238
+ height: number;
239
+ depth: number;
240
+ } | undefined;
241
+ cellCenter2D(slotId: string): {
242
+ x: number;
243
+ y: number;
244
+ } | null;
245
+ slotTargetAt(slotIdOrLocation: string): SlotTarget;
246
+ /**
247
+ * 외부 입력 → 내부 cellId 변환:
248
+ * - "N-N-N" (3 segments, 모두 숫자) → cellId 그대로 (dual-accept)
249
+ * - 그 외 → location 으로 간주, locationIndex lookup
250
+ */
251
+ private _resolveToCellId;
252
+ private _isCellIdFormat;
253
+ /**
254
+ * 한 위치의 cellOverride 를 갱신 (merge). 기존 필드는 유지, 새 필드만 덮어씀.
255
+ * `partial` 의 필드가 *undefined* 면 *그 필드 삭제* (override 키에서 제거).
256
+ */
257
+ setCellOverride(posKey: string, partial: Partial<CellOverride>): void;
258
+ /** 한 위치의 override 전체 삭제. */
259
+ clearCellOverride(posKey: string): void;
260
+ /**
261
+ * 여러 위치의 isEmpty 일괄 토글. cell-component 있으면 cell.set(), 없으면 cellOverrides.
262
+ */
263
+ setIsEmpty(posKeys: string[], isEmpty: boolean): void;
264
+ /**
265
+ * 여러 위치의 border 스타일 일괄 설정. `where` ('top'|'left'|'bottom'|'right'|'all').
266
+ * - style = null → 해당 side 삭제
267
+ * - where = 'all' → 4면 모두 동일 style
268
+ */
269
+ setBorder(posKeys: string[], style: any, where?: 'all' | 'top' | 'left' | 'bottom' | 'right'): void;
270
+ /**
271
+ * 자동 채번 — 선택된 cell 들에 section/unit 자동 부여.
272
+ *
273
+ * @param posKeys 채번 대상 (선택 영역). 빈 array 면 *전체 grid*.
274
+ * @param direction 순회 방향:
275
+ * 'cw' — 한 row 내 col 증가 → 다음 row 는 반대 방향 (boustrophedon)
276
+ * 'ccw' — 반대 방향
277
+ * 'zigzag' — col 우선 증가 (cw 의 90° 회전)
278
+ * 'zigzag-reverse' — 반대
279
+ * @param skipNumbering true 면 isEmpty cell 건너뜀 (단위 번호 안 증가).
280
+ * @param startSection 첫 section 번호 (default 1)
281
+ * @param startUnit 첫 unit 번호 (default 1)
282
+ *
283
+ * Aisle row (모든 cell isEmpty) 는 section 경계로 인식 — section 번호가 다음 row 에서 +1.
284
+ *
285
+ * setState 한 번 (cellOverrides 일괄 갱신) → location 역인덱스 1회 무효화.
286
+ */
287
+ increaseLocation(posKeys: string[], direction?: 'cw' | 'ccw' | 'zigzag' | 'zigzag-reverse', skipNumbering?: boolean, startSection?: number, startUnit?: number): void;
288
+ /**
289
+ * 모든 grid 위치 (전체 columns × rows) 의 posKey 목록.
290
+ */
291
+ private _allPosKeys;
292
+ /**
293
+ * Row 의 col 순회 순서 결정. boustrophedon (S 자형) 패턴 지원.
294
+ * cw : 짝수 row 는 좌→우, 홀수 row 는 우→좌
295
+ * ccw : 짝수 row 는 우→좌, 홀수 row 는 좌→우
296
+ * zigzag : 모든 row 좌→우 (단순)
297
+ * zigzag-reverse : 모든 row 우→좌
298
+ */
299
+ private _orderCols;
80
300
  }
301
+ export {};