@mint-ui/map 1.2.0-test.34 → 1.2.0-test.36

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 (25) hide show
  1. package/dist/components/mint-map/core/advanced/shared/context.d.ts +19 -12
  2. package/dist/components/mint-map/core/advanced/shared/context.js +54 -75
  3. package/dist/components/mint-map/core/advanced/shared/helpers.d.ts +20 -0
  4. package/dist/components/mint-map/core/advanced/shared/helpers.js +40 -0
  5. package/dist/components/mint-map/core/advanced/shared/hooks.d.ts +74 -0
  6. package/dist/components/mint-map/core/advanced/shared/hooks.js +189 -0
  7. package/dist/components/mint-map/core/advanced/shared/index.d.ts +3 -0
  8. package/dist/components/mint-map/core/advanced/shared/performance.d.ts +12 -110
  9. package/dist/components/mint-map/core/advanced/shared/performance.js +56 -151
  10. package/dist/components/mint-map/core/advanced/shared/types.d.ts +18 -153
  11. package/dist/components/mint-map/core/advanced/shared/types.js +0 -1
  12. package/dist/components/mint-map/core/advanced/shared/utils.d.ts +36 -27
  13. package/dist/components/mint-map/core/advanced/shared/utils.js +58 -52
  14. package/dist/components/mint-map/core/advanced/shared/viewport.d.ts +42 -0
  15. package/dist/components/mint-map/core/advanced/shared/viewport.js +51 -0
  16. package/dist/components/mint-map/core/advanced/woongCanvasMarker/WoongCanvasMarker.d.ts +22 -74
  17. package/dist/components/mint-map/core/advanced/woongCanvasMarker/WoongCanvasMarker.js +156 -617
  18. package/dist/components/mint-map/core/advanced/woongCanvasPolygon/WoongCanvasPolygon.d.ts +26 -76
  19. package/dist/components/mint-map/core/advanced/woongCanvasPolygon/WoongCanvasPolygon.js +152 -551
  20. package/dist/components/mint-map/core/advanced/woongCanvasPolygon/renderer.d.ts +67 -8
  21. package/dist/components/mint-map/core/advanced/woongCanvasPolygon/renderer.js +81 -20
  22. package/dist/index.es.js +917 -1575
  23. package/dist/index.js +11 -0
  24. package/dist/index.umd.js +923 -1573
  25. package/package.json +1 -1
@@ -2,24 +2,22 @@ import Konva from "konva";
2
2
  import { Position, Offset } from "../../../types";
3
3
  /**
4
4
  * 캔버스 데이터 타입 Enum
5
- * 마커인지 폴리곤인지 구분하는 상수
6
5
  */
7
6
  export declare enum CanvasDataType {
8
7
  MARKER = "MARKER",
9
8
  POLYGON = "POLYGON"
10
9
  }
11
10
  /**
12
- * 폴리곤 경로 정의
11
+ * 폴리곤 경로 정의 (GeoJSON MultiPolygon 형식)
13
12
  */
14
13
  export interface Paths {
15
14
  type: string;
16
15
  coordinates: number[][][][];
17
16
  }
18
17
  /**
19
- * Konva 캔버스 마커/폴리곤의 기본 필수 속성
20
- * (렌더링에 필요한 최소 정보)
18
+ * 캔버스 마커/폴리곤의 기본 필수 속성
21
19
  */
22
- export interface KonvaCanvasOption {
20
+ export interface CanvasOption {
23
21
  id: string;
24
22
  position: Position;
25
23
  boxWidth?: number;
@@ -30,196 +28,63 @@ export interface KonvaCanvasOption {
30
28
  }
31
29
  /**
32
30
  * 서버 데이터와 캔버스 옵션을 결합한 타입
33
- * @template T 서버에서 받은 원본 데이터 타입 (예: Marker, Polygon)
34
- * @example
35
- * // API에서 받은 Marker 타입을 그대로 유지하면서 캔버스 렌더링 정보 추가
36
- * type MarkerWithCanvas = KonvaCanvasData<Marker>
37
- * // { raId, lat, lng, buildingName, totalArea } + { id, position, boxWidth, ... }
31
+ *
32
+ * @template T 서버에서 받은 원본 데이터 타입
38
33
  */
39
- export declare type KonvaCanvasData<T = {}> = T & KonvaCanvasOption;
34
+ export declare type CanvasData<T = {}> = T & CanvasOption;
40
35
  /**
41
- * 🛠️ 렌더링 유틸리티 함수들
42
- *
43
- * WoongCanvasMarker/WoongCanvasPolygon이 제공하는 헬퍼 함수 모음입니다.
44
- * 커스텀 렌더링 함수 내에서 좌표 변환 시 사용하세요.
45
- *
46
- * ## 주요 기능
47
- * - **자동 캐싱**: 좌표 변환 결과를 LRU 캐시에 저장 (성능 최적화)
48
- * - **지도 좌표 → 화면 좌표**: 위경도를 픽셀 좌표로 자동 변환
49
- * - **null 안전성**: 변환 실패 시 null 반환 (안전한 예외 처리)
36
+ * 렌더링 유틸리티 함수들 (좌표 변환)
50
37
  *
51
38
  * @template T 마커/폴리곤 데이터의 추가 속성 타입
52
- *
53
- * @example
54
- * // 마커 렌더링 예시
55
- * const renderBase = ({ ctx, items, utils }) => {
56
- * for (const item of items) {
57
- * const offset = utils.getOrComputeMarkerOffset(item);
58
- * if (!offset) continue; // 변환 실패 시 스킵
59
- *
60
- * ctx.fillRect(offset.x, offset.y, 50, 50);
61
- * }
62
- * };
63
- *
64
- * @example
65
- * // 폴리곤 렌더링 예시
66
- * const renderBase = ({ ctx, items, utils }) => {
67
- * for (const item of items) {
68
- * const offsets = utils.getOrComputePolygonOffsets(item);
69
- * if (!offsets) continue;
70
- *
71
- * for (const multiPolygon of offsets) {
72
- * for (const polygon of multiPolygon) {
73
- * ctx.beginPath();
74
- * ctx.moveTo(polygon[0][0], polygon[0][1]);
75
- * for (let i = 1; i < polygon.length; i++) {
76
- * ctx.lineTo(polygon[i][0], polygon[i][1]);
77
- * }
78
- * ctx.closePath();
79
- * ctx.fill();
80
- * }
81
- * }
82
- * }
83
- * };
84
39
  */
85
40
  export interface RenderUtils<T> {
86
- /**
87
- * 폴리곤의 위경도 좌표를 화면 픽셀 좌표로 변환합니다.
88
- *
89
- * - **자동 캐싱**: 동일한 폴리곤은 캐시에서 즉시 반환 (성능 최적화)
90
- * - **MultiPolygon 지원**: GeoJSON MultiPolygon 형식 지원
91
- * - **Donut Polygon 지원**: 구멍이 있는 폴리곤 지원
92
- *
93
- * @param polygonData 폴리곤 데이터 (paths 필드 필수)
94
- * @returns 변환된 픽셀 좌표 배열 (4차원 배열) 또는 null (변환 실패 시)
95
- *
96
- * @example
97
- * const offsets = utils.getOrComputePolygonOffsets(polygonItem);
98
- * if (!offsets) return; // 변환 실패
99
- *
100
- * // offsets 구조: [MultiPolygon][Polygon][Point][x/y]
101
- * for (const multiPolygon of offsets) {
102
- * for (const polygon of multiPolygon) {
103
- * ctx.beginPath();
104
- * ctx.moveTo(polygon[0][0], polygon[0][1]);
105
- * for (let i = 1; i < polygon.length; i++) {
106
- * ctx.lineTo(polygon[i][0], polygon[i][1]);
107
- * }
108
- * ctx.closePath();
109
- * ctx.fill();
110
- * }
111
- * }
112
- */
113
- getOrComputePolygonOffsets: (polygonData: KonvaCanvasData<T>) => number[][][][] | null;
114
- /**
115
- * 마커의 위경도 좌표를 화면 픽셀 좌표로 변환합니다.
116
- *
117
- * - **자동 캐싱**: 동일한 마커는 캐시에서 즉시 반환 (성능 최적화)
118
- * - **중심점 기준**: 반환된 좌표는 마커의 중심점 (x, y)
119
- *
120
- * @param markerData 마커 데이터 (position 필드 필수)
121
- * @returns 변환된 픽셀 좌표 { x, y } 또는 null (변환 실패 시)
122
- *
123
- * @example
124
- * const offset = utils.getOrComputeMarkerOffset(markerItem);
125
- * if (!offset) return; // 변환 실패
126
- *
127
- * // offset.x, offset.y는 화면 픽셀 좌표
128
- * const boxWidth = markerItem.boxWidth || 60;
129
- * const boxHeight = markerItem.boxHeight || 75;
130
- *
131
- * // 중앙 정렬, 하단 기준으로 그리기
132
- * ctx.fillRect(
133
- * offset.x - boxWidth / 2,
134
- * offset.y - boxHeight,
135
- * boxWidth,
136
- * boxHeight
137
- * );
138
- */
139
- getOrComputeMarkerOffset: (markerData: KonvaCanvasData<T>) => Offset | null;
41
+ getOrComputePolygonOffsets: (polygonData: CanvasData<T>) => number[][][][] | null;
42
+ getOrComputeMarkerOffset: (markerData: CanvasData<T>) => Offset | null;
140
43
  }
141
44
  /**
142
45
  * 커스텀 렌더링 함수 파라미터 - Base Layer
143
46
  */
144
47
  export interface RenderBaseParams<T> {
145
- /** Canvas 2D 렌더링 컨텍스트 (순수 Canvas API) */
146
48
  ctx: CanvasRenderingContext2D;
147
- /** 렌더링할 마커/폴리곤 데이터 배열 */
148
- items: KonvaCanvasData<T>[];
149
- /** 현재 선택된 마커/폴리곤 ID Set */
49
+ items: CanvasData<T>[];
150
50
  selectedIds: Set<string>;
151
- /** 현재 hover된 마커/폴리곤 데이터 (선택) */
152
- hoveredItem?: KonvaCanvasData<T> | null;
153
- /** 렌더링 유틸리티 함수들 */
51
+ hoveredItem?: CanvasData<T> | null;
154
52
  utils: RenderUtils<T>;
155
53
  }
156
54
  /**
157
55
  * 커스텀 렌더링 함수 타입 - Base Layer
158
56
  *
159
- * 🔥 순수 Canvas API 사용 (Konva 지식 불필요!)
160
- *
161
- * @example
162
- * const renderBase = ({ ctx, items, selectedIds, utils }) => {
163
- * for (const item of items) {
164
- * if (selectedIds.has(item.id)) continue;
165
- * const pos = utils.getOrComputeMarkerOffset(item);
166
- * ctx.fillRect(pos.x, pos.y, 50, 50); // 순수 Canvas API!
167
- * }
168
- * };
57
+ * @template T 마커/폴리곤 데이터의 추가 속성 타입
169
58
  */
170
59
  export declare type CustomRenderBase<T> = (params: RenderBaseParams<T>) => void;
171
60
  /**
172
61
  * 커스텀 렌더링 함수 파라미터 - Animation Layer
173
62
  */
174
63
  export interface RenderAnimationParams<T> {
175
- /** Konva Layer 인스턴스 */
176
64
  layer: Konva.Layer;
177
- /** 현재 선택된 마커/폴리곤 ID Set */
178
65
  selectedIds: Set<string>;
179
- /** 전체 마커/폴리곤 데이터 배열 */
180
- items: KonvaCanvasData<T>[];
181
- /** 렌더링 유틸리티 함수들 */
66
+ items: CanvasData<T>[];
182
67
  utils: RenderUtils<T>;
183
68
  }
184
69
  /**
185
70
  * 커스텀 렌더링 함수 타입 - Animation Layer (선택)
186
71
  *
187
- * @example
188
- * const renderAnimation = ({ layer, selectedIds, items, utils }) => {
189
- * for (const id of selectedIds) {
190
- * const item = items.find(i => i.id === id);
191
- * // Konva 애니메이션 구현
192
- * }
193
- * };
72
+ * @template T 마커/폴리곤 데이터의 추가 속성 타입
194
73
  */
195
74
  export declare type CustomRenderAnimation<T> = (params: RenderAnimationParams<T>) => void;
196
75
  /**
197
76
  * 커스텀 렌더링 함수 파라미터 - Event Layer
198
77
  */
199
78
  export interface RenderEventParams<T> {
200
- /** Canvas 2D 렌더링 컨텍스트 (순수 Canvas API) */
201
79
  ctx: CanvasRenderingContext2D;
202
- /** 현재 hover된 마커/폴리곤 데이터 */
203
- hoveredItem: KonvaCanvasData<T> | null;
204
- /** 렌더링 유틸리티 함수들 */
80
+ hoveredItem: CanvasData<T> | null;
205
81
  utils: RenderUtils<T>;
206
- /** 현재 선택된 마커/폴리곤 데이터 배열 (선택 강조용) */
207
- selectedItems?: KonvaCanvasData<T>[];
208
- /** 외부에서 전달된 단일 선택 아이템 (특별한 효과용) */
209
- selectedItem?: KonvaCanvasData<T> | null;
82
+ selectedItems?: CanvasData<T>[];
83
+ selectedItem?: CanvasData<T> | null;
210
84
  }
211
85
  /**
212
86
  * 커스텀 렌더링 함수 타입 - Event Layer
213
87
  *
214
- * 🔥 순수 Canvas API 사용 (Konva 지식 불필요!)
215
- *
216
- * @example
217
- * const renderEvent = ({ ctx, hoveredItem, utils, selectedItems }) => {
218
- * if (hoveredItem) {
219
- * const pos = utils.getOrComputeMarkerOffset(hoveredItem);
220
- * ctx.fillStyle = 'red';
221
- * ctx.fillRect(pos.x, pos.y, 50, 50); // 순수 Canvas API!
222
- * }
223
- * };
88
+ * @template T 마커/폴리곤 데이터의 추가 속성 타입
224
89
  */
225
90
  export declare type CustomRenderEvent<T> = (params: RenderEventParams<T>) => void;
@@ -4,7 +4,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  /**
6
6
  * 캔버스 데이터 타입 Enum
7
- * 마커인지 폴리곤인지 구분하는 상수
8
7
  */
9
8
  exports.CanvasDataType = void 0;
10
9
 
@@ -1,49 +1,58 @@
1
1
  import { Offset } from "../../../types";
2
2
  import { MintMapController } from "../../MintMapController";
3
- import { KonvaCanvasData } from "./types";
3
+ import { CanvasData } from "./types";
4
4
  /**
5
- * 폴리곤 offset 계산
5
+ * 폴리곤 좌표 변환 (위경도 → 화면 좌표)
6
+ *
7
+ * @param polygonData 폴리곤 데이터
8
+ * @param controller MintMapController 인스턴스
9
+ * @returns 변환된 화면 좌표 배열 (4차원 배열) 또는 null
6
10
  */
7
- export declare const computePolygonOffsets: (polygonData: KonvaCanvasData<any>, controller: MintMapController) => number[][][][] | null;
11
+ export declare const computePolygonOffsets: (polygonData: CanvasData<any>, controller: MintMapController) => number[][][][] | null;
8
12
  /**
9
- * 마커 offset 계산
13
+ * 마커 좌표 변환 (위경도 → 화면 좌표)
14
+ *
15
+ * @param markerData 마커 데이터
16
+ * @param controller MintMapController 인스턴스
17
+ * @returns 변환된 화면 좌표 또는 null
10
18
  */
11
- export declare const computeMarkerOffset: (markerData: KonvaCanvasData<any>, controller: MintMapController) => Offset | null;
19
+ export declare const computeMarkerOffset: (markerData: CanvasData<any>, controller: MintMapController) => Offset | null;
12
20
  /**
13
- * Point-in-Polygon 알고리즘
21
+ * Point-in-Polygon 알고리즘 (Ray Casting)
22
+ *
23
+ * @param point 확인할 점의 좌표
24
+ * @param polygon 폴리곤 좌표 배열
25
+ * @returns 점이 폴리곤 내부에 있으면 true
14
26
  */
15
27
  export declare const isPointInPolygon: (point: Offset, polygon: number[][]) => boolean;
16
28
  /**
17
29
  * 폴리곤 히트 테스트 (도넛 폴리곤 지원)
18
30
  *
19
- * 로직:
20
- * 1. 외부 폴리곤(첫 번째): 내부에 있어야 함
21
- * 2. 내부 구멍들(나머지): 내부에 있으면 안 됨 (evenodd 규칙)
22
- *
23
- * 중요: 도넛 폴리곤과 내부 폴리곤은 별개의 polygonData로 처리됨
24
- * - 도넛 폴리곤 A: isDonutPolygon=true
25
- * - 내부 폴리곤 B: isDonutPolygon=false (별도 데이터)
31
+ * @param clickedOffset 클릭/마우스 위치 좌표
32
+ * @param polygonData 폴리곤 데이터
33
+ * @param getPolygonOffsets 폴리곤 좌표 변환 함수
34
+ * @returns 점이 폴리곤 내부에 있으면 true
26
35
  */
27
- export declare const isPointInPolygonData: (clickedOffset: Offset, polygonData: KonvaCanvasData<any>, getPolygonOffsets: (data: KonvaCanvasData<any>) => number[][][][] | null) => boolean;
36
+ export declare const isPointInPolygonData: (clickedOffset: Offset, polygonData: CanvasData<any>, getPolygonOffsets: (data: CanvasData<any>) => number[][][][] | null) => boolean;
28
37
  /**
29
- * 마커 히트 테스트 (클릭/hover 영역 체크)
38
+ * 마커 히트 테스트 (꼬리 제외)
30
39
  *
31
- * 🎯 중요: 꼬리(tail)는 Hit Test 영역에서 제외됩니다!
32
- * - markerOffset.y는 마커 최하단(꼬리 끝) 좌표
33
- * - boxHeight는 마커 본체만 포함 (꼬리 제외)
34
- * - tailHeight만큼 위로 올려서 본체만 Hit Test 영역으로 사용
40
+ * @param clickedOffset 클릭/마우스 위치 좌표
41
+ * @param markerData 마커 데이터
42
+ * @param getMarkerOffset 마커 좌표 변환 함수
43
+ * @returns 점이 마커 영역 내부에 있으면 true
35
44
  */
36
- export declare const isPointInMarkerData: (clickedOffset: Offset, markerData: KonvaCanvasData<any>, getMarkerOffset: (data: KonvaCanvasData<any>) => Offset | null) => boolean;
45
+ export declare const isPointInMarkerData: (clickedOffset: Offset, markerData: CanvasData<any>, getMarkerOffset: (data: CanvasData<any>) => Offset | null) => boolean;
37
46
  export declare const hexToRgba: (hexColor: string, alpha?: number) => string;
38
47
  /**
39
- * 텍스트 박스의 너비를 계산합니다.
48
+ * 텍스트 박스 너비 계산
40
49
  *
41
- * @param {Object} params - 파라미터 객체
42
- * @param {string} params.text - 측정할 텍스트
43
- * @param {string} params.fontConfig - 폰트 설정 (예: 'bold 16px Arial')
44
- * @param {number} params.padding - 텍스트 박스에 적용할 패딩 값
45
- * @param {number} params.minWidth - 최소 너비
46
- * @returns {number} 계산된 텍스트 박스의 너비
50
+ * @param params 파라미터 객체
51
+ * @param params.text 측정할 텍스트
52
+ * @param params.fontConfig 폰트 설정
53
+ * @param params.padding 패딩 값 (px)
54
+ * @param params.minWidth 최소 너비 (px)
55
+ * @returns 계산된 텍스트 박스 너비 (px)
47
56
  */
48
57
  export declare const calculateTextBoxWidth: ({ text, fontConfig, padding, minWidth, }: {
49
58
  text: string;
@@ -7,7 +7,11 @@ var MapTypes = require('../../../types/MapTypes.js');
7
7
  require('../../../types/MapEventTypes.js');
8
8
 
9
9
  /**
10
- * 폴리곤 offset 계산
10
+ * 폴리곤 좌표 변환 (위경도 → 화면 좌표)
11
+ *
12
+ * @param polygonData 폴리곤 데이터
13
+ * @param controller MintMapController 인스턴스
14
+ * @returns 변환된 화면 좌표 배열 (4차원 배열) 또는 null
11
15
  */
12
16
 
13
17
  var computePolygonOffsets = function (polygonData, controller) {
@@ -17,7 +21,7 @@ var computePolygonOffsets = function (polygonData, controller) {
17
21
  return null;
18
22
  }
19
23
 
20
- var result = [];
24
+ var result = []; // GeoJSON MultiPolygon 구조: [MultiPolygon][PolygonGroup][Coordinate][lng, lat]
21
25
 
22
26
  for (var _i = 0, _a = paths.coordinates; _i < _a.length; _i++) {
23
27
  var multiPolygon = _a[_i];
@@ -28,7 +32,8 @@ var computePolygonOffsets = function (polygonData, controller) {
28
32
  var polygonOffsets = [];
29
33
 
30
34
  for (var _c = 0, polygonGroup_1 = polygonGroup; _c < polygonGroup_1.length; _c++) {
31
- var coord = polygonGroup_1[_c];
35
+ var coord = polygonGroup_1[_c]; // GeoJSON은 [lng, lat] 순서이지만 Position은 [lat, lng] 순서
36
+
32
37
  var pos = new MapTypes.Position(coord[1], coord[0]);
33
38
  var offset = controller.positionToOffset(pos);
34
39
  polygonOffsets.push([offset.x, offset.y]);
@@ -43,30 +48,37 @@ var computePolygonOffsets = function (polygonData, controller) {
43
48
  return result;
44
49
  };
45
50
  /**
46
- * 마커 offset 계산
51
+ * 마커 좌표 변환 (위경도 → 화면 좌표)
52
+ *
53
+ * @param markerData 마커 데이터
54
+ * @param controller MintMapController 인스턴스
55
+ * @returns 변환된 화면 좌표 또는 null
47
56
  */
48
57
 
49
58
  var computeMarkerOffset = function (markerData, controller) {
50
- if (!markerData.position) {
51
- return null;
52
- }
53
-
59
+ if (!markerData.position) return null;
54
60
  return controller.positionToOffset(markerData.position);
55
61
  };
56
62
  /**
57
- * Point-in-Polygon 알고리즘
63
+ * Point-in-Polygon 알고리즘 (Ray Casting)
64
+ *
65
+ * @param point 확인할 점의 좌표
66
+ * @param polygon 폴리곤 좌표 배열
67
+ * @returns 점이 폴리곤 내부에 있으면 true
58
68
  */
59
69
 
60
70
  var isPointInPolygon = function (point, polygon) {
71
+ // Ray Casting 알고리즘: 점에서 오른쪽으로 무한히 뻗은 선과 폴리곤 변의 교차 횟수로 판단
61
72
  var inside = false;
62
73
 
63
74
  for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
64
75
  var xi = polygon[i][0],
65
76
  yi = polygon[i][1];
66
77
  var xj = polygon[j][0],
67
- yj = polygon[j][1];
78
+ yj = polygon[j][1]; // 점의 y 좌표가 변의 양 끝점 사이에 있고, 교차점의 x 좌표가 점의 x 좌표보다 큰지 확인
79
+
68
80
  var intersect = yi > point.y !== yj > point.y && point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi;
69
- if (intersect) inside = !inside;
81
+ if (intersect) inside = !inside; // 교차할 때마다 inside 상태 토글
70
82
  }
71
83
 
72
84
  return inside;
@@ -74,23 +86,20 @@ var isPointInPolygon = function (point, polygon) {
74
86
  /**
75
87
  * 폴리곤 히트 테스트 (도넛 폴리곤 지원)
76
88
  *
77
- * 로직:
78
- * 1. 외부 폴리곤(첫 번째): 내부에 있어야 함
79
- * 2. 내부 구멍들(나머지): 내부에 있으면 안 됨 (evenodd 규칙)
80
- *
81
- * 중요: 도넛 폴리곤과 내부 폴리곤은 별개의 polygonData로 처리됨
82
- * - 도넛 폴리곤 A: isDonutPolygon=true
83
- * - 내부 폴리곤 B: isDonutPolygon=false (별도 데이터)
89
+ * @param clickedOffset 클릭/마우스 위치 좌표
90
+ * @param polygonData 폴리곤 데이터
91
+ * @param getPolygonOffsets 폴리곤 좌표 변환 함수
92
+ * @returns 점이 폴리곤 내부에 있으면 true
84
93
  */
85
94
 
86
95
  var isPointInPolygonData = function (clickedOffset, polygonData, getPolygonOffsets) {
87
96
  var polygonOffsets = getPolygonOffsets(polygonData);
88
- if (!polygonOffsets) return false; // 🍩 도넛 폴리곤 처리 (isDonutPolygon === true)
97
+ if (!polygonOffsets) return false; // 도넛 폴리곤 처리: 외부 폴리곤 내부에 있으면서 구멍(hole) 내부에 있지 않아야 함
89
98
 
90
99
  if (polygonData.isDonutPolygon) {
91
100
  for (var _i = 0, polygonOffsets_1 = polygonOffsets; _i < polygonOffsets_1.length; _i++) {
92
101
  var multiPolygon = polygonOffsets_1[_i];
93
- if (multiPolygon.length === 0) continue; // 외부 폴리곤만 있는 경우 (구멍 없음) - 일반 폴리곤처럼 처리
102
+ if (multiPolygon.length === 0) continue; // 구멍이 없는 경우 일반 폴리곤과 동일
94
103
 
95
104
  if (multiPolygon.length === 1) {
96
105
  if (isPointInPolygon(clickedOffset, multiPolygon[0])) {
@@ -98,33 +107,30 @@ var isPointInPolygonData = function (clickedOffset, polygonData, getPolygonOffse
98
107
  }
99
108
 
100
109
  continue;
101
- } // 1. 외부 폴리곤(첫 번째)에 포함되는지 확인
110
+ } // 외부 폴리곤 내부에 있는지 확인
102
111
 
103
112
 
104
113
  var outerPolygon = multiPolygon[0];
105
114
 
106
115
  if (!isPointInPolygon(clickedOffset, outerPolygon)) {
107
- continue; // 외부 폴리곤 밖이면 다음 multiPolygon 확인
108
- } // 2. 내부 구멍들(나머지)에 포함되는지 확인
116
+ continue;
117
+ } // 구멍 내부에 있으면 false (도넛의 공간)
109
118
 
110
119
 
111
120
  for (var i = 1; i < multiPolygon.length; i++) {
112
121
  var hole = multiPolygon[i];
113
122
 
114
123
  if (isPointInPolygon(clickedOffset, hole)) {
115
- // ❌ 구멍 안에 있음 → 이 도넛 폴리곤은 히트 안 됨
116
- // 다른 multiPolygon 체크하지 않고 바로 false 반환
117
- // (도넛 폴리곤의 구멍 안은 무조건 클릭 불가)
118
124
  return false;
119
125
  }
120
- } // 외부 폴리곤 + 구멍 = 히트!
126
+ } // 외부 폴리곤 내부에 있으면서 모든 구멍 밖에 있으면 true
121
127
 
122
128
 
123
129
  return true;
124
130
  }
125
131
 
126
132
  return false;
127
- } // 일반 폴리곤 처리 (isDonutPolygon === false 또는 undefined)
133
+ } // 일반 폴리곤 처리
128
134
 
129
135
 
130
136
  for (var _a = 0, polygonOffsets_2 = polygonOffsets; _a < polygonOffsets_2.length; _a++) {
@@ -143,12 +149,12 @@ var isPointInPolygonData = function (clickedOffset, polygonData, getPolygonOffse
143
149
  return false;
144
150
  };
145
151
  /**
146
- * 마커 히트 테스트 (클릭/hover 영역 체크)
152
+ * 마커 히트 테스트 (꼬리 제외)
147
153
  *
148
- * 🎯 중요: 꼬리(tail)는 Hit Test 영역에서 제외됩니다!
149
- * - markerOffset.y는 마커 최하단(꼬리 끝) 좌표
150
- * - boxHeight는 마커 본체만 포함 (꼬리 제외)
151
- * - tailHeight만큼 위로 올려서 본체만 Hit Test 영역으로 사용
154
+ * @param clickedOffset 클릭/마우스 위치 좌표
155
+ * @param markerData 마커 데이터
156
+ * @param getMarkerOffset 마커 좌표 변환 함수
157
+ * @returns 점이 마커 영역 내부에 있으면 true
152
158
  */
153
159
 
154
160
  var isPointInMarkerData = function (clickedOffset, markerData, getMarkerOffset) {
@@ -156,41 +162,41 @@ var isPointInMarkerData = function (clickedOffset, markerData, getMarkerOffset)
156
162
  if (!markerOffset) return false;
157
163
  var boxWidth = markerData.boxWidth || 50;
158
164
  var boxHeight = markerData.boxHeight || 28;
159
- var tailHeight = markerData.tailHeight || 0; // 🎯 tailHeight 사용!
165
+ var tailHeight = markerData.tailHeight || 0; // 마커 중심점 기준으로 박스 영역 계산 (꼬리는 제외)
160
166
 
161
167
  var x = markerOffset.x - boxWidth / 2;
162
- var y = markerOffset.y - boxHeight - tailHeight; // 🔥 꼬리만큼 위로!
168
+ var y = markerOffset.y - boxHeight - tailHeight; // 클릭 위치가 박스 영역 내부에 있는지 확인
163
169
 
164
170
  return clickedOffset.x >= x && clickedOffset.x <= x + boxWidth && clickedOffset.y >= y && clickedOffset.y <= y + boxHeight;
165
- };
171
+ }; // Hex 색상을 RGBA로 변환
172
+
166
173
  var hexToRgba = function (hexColor, alpha) {
167
174
  if (alpha === void 0) {
168
175
  alpha = 1;
169
- } // NOTE: 입력된 hexColor에서 "#" 제거
170
-
176
+ }
171
177
 
172
- var hex = hexColor.replace('#', ''); // NOTE: 6자리일 경우 알파 값은 사용자가 제공한 alpha 값으로 설정
178
+ var hex = hexColor.replace('#', '');
173
179
 
174
- if (hex.length === 6) {
175
- var r = parseInt(hex.substring(0, 2), 16);
176
- var g = parseInt(hex.substring(2, 4), 16);
177
- var b = parseInt(hex.substring(4, 6), 16);
178
- return "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(alpha, ")");
180
+ if (hex.length !== 6) {
181
+ throw new Error('Invalid hex color format');
179
182
  }
180
183
 
181
- throw new Error('Invalid hex color format');
184
+ var r = parseInt(hex.substring(0, 2), 16);
185
+ var g = parseInt(hex.substring(2, 4), 16);
186
+ var b = parseInt(hex.substring(4, 6), 16);
187
+ return "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(alpha, ")");
182
188
  };
183
189
  var tempCanvas = document.createElement('canvas');
184
190
  var tempCtx = tempCanvas.getContext('2d');
185
191
  /**
186
- * 텍스트 박스의 너비를 계산합니다.
192
+ * 텍스트 박스 너비 계산
187
193
  *
188
- * @param {Object} params - 파라미터 객체
189
- * @param {string} params.text - 측정할 텍스트
190
- * @param {string} params.fontConfig - 폰트 설정 (예: 'bold 16px Arial')
191
- * @param {number} params.padding - 텍스트 박스에 적용할 패딩 값
192
- * @param {number} params.minWidth - 최소 너비
193
- * @returns {number} 계산된 텍스트 박스의 너비
194
+ * @param params 파라미터 객체
195
+ * @param params.text 측정할 텍스트
196
+ * @param params.fontConfig 폰트 설정
197
+ * @param params.padding 패딩 값 (px)
198
+ * @param params.minWidth 최소 너비 (px)
199
+ * @returns 계산된 텍스트 박스 너비 (px)
194
200
  */
195
201
 
196
202
  var calculateTextBoxWidth = function (_a) {
@@ -0,0 +1,42 @@
1
+ import { MutableRefObject } from "react";
2
+ import { CanvasData } from "./types";
3
+ /**
4
+ * 뷰포트 영역 (화면에 보이는 영역)
5
+ */
6
+ export interface ViewportBounds {
7
+ minX: number;
8
+ maxX: number;
9
+ minY: number;
10
+ maxY: number;
11
+ }
12
+ /**
13
+ * 바운딩 박스 (마커/폴리곤의 최소/최대 좌표)
14
+ */
15
+ export interface BoundingBox {
16
+ minX: number;
17
+ minY: number;
18
+ maxX: number;
19
+ maxY: number;
20
+ }
21
+ /**
22
+ * 현재 뷰포트 영역 계산
23
+ *
24
+ * @param stage Konva Stage 인스턴스
25
+ * @param cullingMargin 컬링 여유 공간 (px)
26
+ * @param viewportRef 뷰포트 경계를 저장할 ref
27
+ */
28
+ export declare const updateViewport: (stage: {
29
+ width: () => number;
30
+ height: () => number;
31
+ } | null, cullingMargin: number, viewportRef: MutableRefObject<ViewportBounds | null>) => void;
32
+ /**
33
+ * 아이템이 현재 뷰포트 안에 있는지 확인 (바운딩 박스 캐싱)
34
+ *
35
+ * @template T 마커/폴리곤 데이터의 추가 속성 타입
36
+ * @param item 확인할 아이템
37
+ * @param viewportRef 뷰포트 경계 ref
38
+ * @param boundingBoxCacheRef 바운딩 박스 캐시 ref
39
+ * @param computeBoundingBox 바운딩 박스 계산 함수
40
+ * @returns 뷰포트 안에 있으면 true
41
+ */
42
+ export declare const isInViewport: <T>(item: CanvasData<T>, viewportRef: MutableRefObject<ViewportBounds | null>, boundingBoxCacheRef: MutableRefObject<Map<string, BoundingBox>>, computeBoundingBox: (item: CanvasData<T>) => BoundingBox | null) => boolean;
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ /**
6
+ * 현재 뷰포트 영역 계산
7
+ *
8
+ * @param stage Konva Stage 인스턴스
9
+ * @param cullingMargin 컬링 여유 공간 (px)
10
+ * @param viewportRef 뷰포트 경계를 저장할 ref
11
+ */
12
+ var updateViewport = function (stage, cullingMargin, viewportRef) {
13
+ if (!stage) return;
14
+ viewportRef.current = {
15
+ minX: -cullingMargin,
16
+ maxX: stage.width() + cullingMargin,
17
+ minY: -cullingMargin,
18
+ maxY: stage.height() + cullingMargin
19
+ };
20
+ };
21
+ /**
22
+ * 아이템이 현재 뷰포트 안에 있는지 확인 (바운딩 박스 캐싱)
23
+ *
24
+ * @template T 마커/폴리곤 데이터의 추가 속성 타입
25
+ * @param item 확인할 아이템
26
+ * @param viewportRef 뷰포트 경계 ref
27
+ * @param boundingBoxCacheRef 바운딩 박스 캐시 ref
28
+ * @param computeBoundingBox 바운딩 박스 계산 함수
29
+ * @returns 뷰포트 안에 있으면 true
30
+ */
31
+
32
+ var isInViewport = function (item, viewportRef, boundingBoxCacheRef, computeBoundingBox) {
33
+ if (!viewportRef.current) return true;
34
+ var viewport = viewportRef.current; // 캐시된 바운딩 박스 확인
35
+
36
+ var bbox = boundingBoxCacheRef.current.get(item.id);
37
+
38
+ if (!bbox) {
39
+ // 바운딩 박스 계산 (공통 함수 사용)
40
+ var computed = computeBoundingBox(item);
41
+ if (!computed) return false;
42
+ bbox = computed;
43
+ boundingBoxCacheRef.current.set(item.id, bbox);
44
+ } // 바운딩 박스와 viewport 교차 체크
45
+
46
+
47
+ return !(bbox.maxX < viewport.minX || bbox.minX > viewport.maxX || bbox.maxY < viewport.minY || bbox.minY > viewport.maxY);
48
+ };
49
+
50
+ exports.isInViewport = isInViewport;
51
+ exports.updateViewport = updateViewport;