@mint-ui/map 1.2.0-test.35 → 1.2.0-test.37
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/components/mint-map/core/advanced/shared/context.d.ts +9 -71
- package/dist/components/mint-map/core/advanced/shared/context.js +43 -137
- package/dist/components/mint-map/core/advanced/shared/helpers.d.ts +5 -13
- package/dist/components/mint-map/core/advanced/shared/helpers.js +8 -20
- package/dist/components/mint-map/core/advanced/shared/hooks.d.ts +6 -76
- package/dist/components/mint-map/core/advanced/shared/hooks.js +18 -112
- package/dist/components/mint-map/core/advanced/shared/performance.d.ts +9 -188
- package/dist/components/mint-map/core/advanced/shared/performance.js +53 -229
- package/dist/components/mint-map/core/advanced/shared/types.d.ts +22 -153
- package/dist/components/mint-map/core/advanced/shared/types.js +0 -1
- package/dist/components/mint-map/core/advanced/shared/utils.d.ts +21 -126
- package/dist/components/mint-map/core/advanced/shared/utils.js +46 -152
- package/dist/components/mint-map/core/advanced/shared/viewport.d.ts +4 -34
- package/dist/components/mint-map/core/advanced/shared/viewport.js +4 -34
- package/dist/components/mint-map/core/advanced/woongCanvasMarker/WoongCanvasMarker.d.ts +22 -74
- package/dist/components/mint-map/core/advanced/woongCanvasMarker/WoongCanvasMarker.js +128 -519
- package/dist/components/mint-map/core/advanced/woongCanvasPolygon/WoongCanvasPolygon.d.ts +26 -76
- package/dist/components/mint-map/core/advanced/woongCanvasPolygon/WoongCanvasPolygon.js +118 -432
- package/dist/components/mint-map/core/advanced/woongCanvasPolygon/renderer.d.ts +3 -3
- package/dist/index.es.js +419 -1637
- package/dist/index.umd.js +419 -1637
- package/package.json +1 -1
|
@@ -1,163 +1,58 @@
|
|
|
1
1
|
import { Offset } from "../../../types";
|
|
2
2
|
import { MintMapController } from "../../MintMapController";
|
|
3
|
-
import {
|
|
3
|
+
import { CanvasData } from "./types";
|
|
4
4
|
/**
|
|
5
|
-
* 폴리곤
|
|
5
|
+
* 폴리곤 좌표 변환 (위경도 → 화면 좌표)
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* @param polygonData 폴리곤 데이터 (paths 필드 필수)
|
|
7
|
+
* @param polygonData 폴리곤 데이터
|
|
10
8
|
* @param controller MintMapController 인스턴스
|
|
11
|
-
* @returns 변환된 화면 좌표 배열 (4차원 배열) 또는 null
|
|
12
|
-
*
|
|
13
|
-
* @remarks
|
|
14
|
-
* - 반환 형식: [MultiPolygon][Polygon][Point][x/y]
|
|
15
|
-
* - 성능: O(n), n은 폴리곤의 총 좌표 수
|
|
16
|
-
* - GeoJSON MultiPolygon 형식 지원
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```typescript
|
|
20
|
-
* const offsets = computePolygonOffsets(polygonData, controller);
|
|
21
|
-
* if (!offsets) return; // 변환 실패
|
|
22
|
-
*
|
|
23
|
-
* // offsets 구조: [MultiPolygon][Polygon][Point][x/y]
|
|
24
|
-
* for (const multiPolygon of offsets) {
|
|
25
|
-
* for (const polygon of multiPolygon) {
|
|
26
|
-
* // polygon은 [Point][x/y] 배열
|
|
27
|
-
* }
|
|
28
|
-
* }
|
|
29
|
-
* ```
|
|
9
|
+
* @returns 변환된 화면 좌표 배열 (4차원 배열) 또는 null
|
|
30
10
|
*/
|
|
31
|
-
export declare const computePolygonOffsets: (polygonData:
|
|
11
|
+
export declare const computePolygonOffsets: (polygonData: CanvasData<any>, controller: MintMapController) => number[][][][] | null;
|
|
32
12
|
/**
|
|
33
|
-
* 마커
|
|
34
|
-
*
|
|
35
|
-
* 마커의 위경도 좌표를 화면 픽셀 좌표로 변환합니다.
|
|
13
|
+
* 마커 좌표 변환 (위경도 → 화면 좌표)
|
|
36
14
|
*
|
|
37
|
-
* @param markerData 마커 데이터
|
|
15
|
+
* @param markerData 마커 데이터
|
|
38
16
|
* @param controller MintMapController 인스턴스
|
|
39
|
-
* @returns 변환된 화면 좌표
|
|
40
|
-
*
|
|
41
|
-
* @remarks
|
|
42
|
-
* - 반환된 좌표는 마커의 중심점 (x, y)
|
|
43
|
-
* - 성능: O(1) - 단일 좌표 변환
|
|
44
|
-
*
|
|
45
|
-
* @example
|
|
46
|
-
* ```typescript
|
|
47
|
-
* const offset = computeMarkerOffset(markerData, controller);
|
|
48
|
-
* if (!offset) return; // 변환 실패
|
|
49
|
-
* // offset.x, offset.y는 화면 픽셀 좌표
|
|
50
|
-
* ```
|
|
17
|
+
* @returns 변환된 화면 좌표 또는 null
|
|
51
18
|
*/
|
|
52
|
-
export declare const computeMarkerOffset: (markerData:
|
|
19
|
+
export declare const computeMarkerOffset: (markerData: CanvasData<any>, controller: MintMapController) => Offset | null;
|
|
53
20
|
/**
|
|
54
|
-
* Point-in-Polygon 알고리즘
|
|
55
|
-
*
|
|
56
|
-
* Ray Casting 알고리즘을 사용하여 점이 폴리곤 내부에 있는지 확인합니다.
|
|
21
|
+
* Point-in-Polygon 알고리즘 (Ray Casting)
|
|
57
22
|
*
|
|
58
23
|
* @param point 확인할 점의 좌표
|
|
59
|
-
* @param polygon 폴리곤 좌표 배열
|
|
60
|
-
* @returns 점이 폴리곤 내부에 있으면 true
|
|
61
|
-
*
|
|
62
|
-
* @remarks
|
|
63
|
-
* - **알고리즘**: Ray Casting (Ray Crossing)
|
|
64
|
-
* - **성능**: O(n), n은 폴리곤의 좌표 수
|
|
65
|
-
* - **경계 처리**: 경계선 위의 점은 내부로 간주
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* ```typescript
|
|
69
|
-
* const point = { x: 100, y: 200 };
|
|
70
|
-
* const polygon = [[0, 0], [100, 0], [100, 100], [0, 100]];
|
|
71
|
-
* const isInside = isPointInPolygon(point, polygon);
|
|
72
|
-
* ```
|
|
24
|
+
* @param polygon 폴리곤 좌표 배열
|
|
25
|
+
* @returns 점이 폴리곤 내부에 있으면 true
|
|
73
26
|
*/
|
|
74
27
|
export declare const isPointInPolygon: (point: Offset, polygon: number[][]) => boolean;
|
|
75
28
|
/**
|
|
76
29
|
* 폴리곤 히트 테스트 (도넛 폴리곤 지원)
|
|
77
30
|
*
|
|
78
|
-
* 점이 폴리곤 내부에 있는지 확인합니다. 도넛 폴리곤(구멍이 있는 폴리곤)을 지원합니다.
|
|
79
|
-
*
|
|
80
31
|
* @param clickedOffset 클릭/마우스 위치 좌표
|
|
81
32
|
* @param polygonData 폴리곤 데이터
|
|
82
33
|
* @param getPolygonOffsets 폴리곤 좌표 변환 함수
|
|
83
|
-
* @returns 점이 폴리곤 내부에 있으면 true
|
|
84
|
-
*
|
|
85
|
-
* @remarks
|
|
86
|
-
* - **도넛 폴리곤 처리** (isDonutPolygon === true):
|
|
87
|
-
* 1. 외부 폴리곤(첫 번째): 내부에 있어야 함
|
|
88
|
-
* 2. 내부 구멍들(나머지): 내부에 있으면 안 됨 (evenodd 규칙)
|
|
89
|
-
* - **일반 폴리곤 처리**: Point-in-Polygon 알고리즘 사용
|
|
90
|
-
* - **성능**: O(n), n은 폴리곤의 총 좌표 수
|
|
91
|
-
*
|
|
92
|
-
* **중요**: 도넛 폴리곤과 내부 폴리곤은 별개의 polygonData로 처리됩니다.
|
|
93
|
-
* - 도넛 폴리곤 A: isDonutPolygon=true
|
|
94
|
-
* - 내부 폴리곤 B: isDonutPolygon=false (별도 데이터)
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* ```typescript
|
|
98
|
-
* const isHit = isPointInPolygonData(
|
|
99
|
-
* clickedOffset,
|
|
100
|
-
* polygonData,
|
|
101
|
-
* getOrComputePolygonOffsets
|
|
102
|
-
* );
|
|
103
|
-
* ```
|
|
34
|
+
* @returns 점이 폴리곤 내부에 있으면 true
|
|
104
35
|
*/
|
|
105
|
-
export declare const isPointInPolygonData: (clickedOffset: Offset, polygonData:
|
|
36
|
+
export declare const isPointInPolygonData: (clickedOffset: Offset, polygonData: CanvasData<any>, getPolygonOffsets: (data: CanvasData<any>) => number[][][][] | null) => boolean;
|
|
106
37
|
/**
|
|
107
|
-
* 마커 히트 테스트 (
|
|
108
|
-
*
|
|
109
|
-
* 점이 마커의 클릭/호버 영역 내부에 있는지 확인합니다.
|
|
110
|
-
* 마커의 꼬리(tail)는 Hit Test 영역에서 제외됩니다.
|
|
38
|
+
* 마커 히트 테스트 (꼬리 제외, 오프셋 지원)
|
|
111
39
|
*
|
|
112
40
|
* @param clickedOffset 클릭/마우스 위치 좌표
|
|
113
41
|
* @param markerData 마커 데이터
|
|
114
42
|
* @param getMarkerOffset 마커 좌표 변환 함수
|
|
115
|
-
* @returns 점이 마커 영역 내부에 있으면 true
|
|
116
|
-
*
|
|
117
|
-
* @remarks
|
|
118
|
-
* - **꼬리 제외**: 꼬리(tail)는 Hit Test 영역에서 제외됩니다
|
|
119
|
-
* - markerOffset.y는 마커 최하단(꼬리 끝) 좌표
|
|
120
|
-
* - boxHeight는 마커 본체만 포함 (꼬리 제외)
|
|
121
|
-
* - tailHeight만큼 위로 올려서 본체만 Hit Test 영역으로 사용
|
|
122
|
-
* - **성능**: O(1) - 단순 사각형 영역 체크
|
|
123
|
-
*
|
|
124
|
-
* @example
|
|
125
|
-
* ```typescript
|
|
126
|
-
* const isHit = isPointInMarkerData(
|
|
127
|
-
* clickedOffset,
|
|
128
|
-
* markerData,
|
|
129
|
-
* getOrComputeMarkerOffset
|
|
130
|
-
* );
|
|
131
|
-
* ```
|
|
43
|
+
* @returns 점이 마커 영역 내부에 있으면 true
|
|
132
44
|
*/
|
|
133
|
-
export declare const isPointInMarkerData: (clickedOffset: Offset, markerData:
|
|
45
|
+
export declare const isPointInMarkerData: (clickedOffset: Offset, markerData: CanvasData<any>, getMarkerOffset: (data: CanvasData<any>) => Offset | null) => boolean;
|
|
134
46
|
export declare const hexToRgba: (hexColor: string, alpha?: number) => string;
|
|
135
47
|
/**
|
|
136
|
-
* 텍스트
|
|
137
|
-
*
|
|
138
|
-
* Canvas 2D Context의 measureText()를 사용하여 텍스트의 실제 너비를 계산하고,
|
|
139
|
-
* 패딩과 최소 너비를 고려하여 최종 너비를 반환합니다.
|
|
48
|
+
* 텍스트 박스 너비 계산
|
|
140
49
|
*
|
|
141
50
|
* @param params 파라미터 객체
|
|
142
51
|
* @param params.text 측정할 텍스트
|
|
143
|
-
* @param params.fontConfig 폰트 설정
|
|
144
|
-
* @param params.padding
|
|
52
|
+
* @param params.fontConfig 폰트 설정
|
|
53
|
+
* @param params.padding 패딩 값 (px)
|
|
145
54
|
* @param params.minWidth 최소 너비 (px)
|
|
146
|
-
* @returns 계산된 텍스트
|
|
147
|
-
*
|
|
148
|
-
* @remarks
|
|
149
|
-
* - 성능: O(1) - 단일 텍스트 측정
|
|
150
|
-
* - 임시 Canvas를 사용하여 정확한 너비 측정
|
|
151
|
-
*
|
|
152
|
-
* @example
|
|
153
|
-
* ```typescript
|
|
154
|
-
* const width = calculateTextBoxWidth({
|
|
155
|
-
* text: "Hello World",
|
|
156
|
-
* fontConfig: 'bold 16px Arial',
|
|
157
|
-
* padding: 20,
|
|
158
|
-
* minWidth: 60
|
|
159
|
-
* });
|
|
160
|
-
* ```
|
|
55
|
+
* @returns 계산된 텍스트 박스 너비 (px)
|
|
161
56
|
*/
|
|
162
57
|
export declare const calculateTextBoxWidth: ({ text, fontConfig, padding, minWidth, }: {
|
|
163
58
|
text: string;
|
|
@@ -7,31 +7,11 @@ var MapTypes = require('../../../types/MapTypes.js');
|
|
|
7
7
|
require('../../../types/MapEventTypes.js');
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* 폴리곤
|
|
10
|
+
* 폴리곤 좌표 변환 (위경도 → 화면 좌표)
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* @param polygonData 폴리곤 데이터 (paths 필드 필수)
|
|
12
|
+
* @param polygonData 폴리곤 데이터
|
|
15
13
|
* @param controller MintMapController 인스턴스
|
|
16
|
-
* @returns 변환된 화면 좌표 배열 (4차원 배열) 또는 null
|
|
17
|
-
*
|
|
18
|
-
* @remarks
|
|
19
|
-
* - 반환 형식: [MultiPolygon][Polygon][Point][x/y]
|
|
20
|
-
* - 성능: O(n), n은 폴리곤의 총 좌표 수
|
|
21
|
-
* - GeoJSON MultiPolygon 형식 지원
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* ```typescript
|
|
25
|
-
* const offsets = computePolygonOffsets(polygonData, controller);
|
|
26
|
-
* if (!offsets) return; // 변환 실패
|
|
27
|
-
*
|
|
28
|
-
* // offsets 구조: [MultiPolygon][Polygon][Point][x/y]
|
|
29
|
-
* for (const multiPolygon of offsets) {
|
|
30
|
-
* for (const polygon of multiPolygon) {
|
|
31
|
-
* // polygon은 [Point][x/y] 배열
|
|
32
|
-
* }
|
|
33
|
-
* }
|
|
34
|
-
* ```
|
|
14
|
+
* @returns 변환된 화면 좌표 배열 (4차원 배열) 또는 null
|
|
35
15
|
*/
|
|
36
16
|
|
|
37
17
|
var computePolygonOffsets = function (polygonData, controller) {
|
|
@@ -41,7 +21,7 @@ var computePolygonOffsets = function (polygonData, controller) {
|
|
|
41
21
|
return null;
|
|
42
22
|
}
|
|
43
23
|
|
|
44
|
-
var result = [];
|
|
24
|
+
var result = []; // GeoJSON MultiPolygon 구조: [MultiPolygon][PolygonGroup][Coordinate][lng, lat]
|
|
45
25
|
|
|
46
26
|
for (var _i = 0, _a = paths.coordinates; _i < _a.length; _i++) {
|
|
47
27
|
var multiPolygon = _a[_i];
|
|
@@ -52,7 +32,8 @@ var computePolygonOffsets = function (polygonData, controller) {
|
|
|
52
32
|
var polygonOffsets = [];
|
|
53
33
|
|
|
54
34
|
for (var _c = 0, polygonGroup_1 = polygonGroup; _c < polygonGroup_1.length; _c++) {
|
|
55
|
-
var coord = polygonGroup_1[_c];
|
|
35
|
+
var coord = polygonGroup_1[_c]; // GeoJSON은 [lng, lat] 순서이지만 Position은 [lat, lng] 순서
|
|
36
|
+
|
|
56
37
|
var pos = new MapTypes.Position(coord[1], coord[0]);
|
|
57
38
|
var offset = controller.positionToOffset(pos);
|
|
58
39
|
polygonOffsets.push([offset.x, offset.y]);
|
|
@@ -67,65 +48,37 @@ var computePolygonOffsets = function (polygonData, controller) {
|
|
|
67
48
|
return result;
|
|
68
49
|
};
|
|
69
50
|
/**
|
|
70
|
-
* 마커
|
|
51
|
+
* 마커 좌표 변환 (위경도 → 화면 좌표)
|
|
71
52
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* @param markerData 마커 데이터 (position 필드 필수)
|
|
53
|
+
* @param markerData 마커 데이터
|
|
75
54
|
* @param controller MintMapController 인스턴스
|
|
76
|
-
* @returns 변환된 화면 좌표
|
|
77
|
-
*
|
|
78
|
-
* @remarks
|
|
79
|
-
* - 반환된 좌표는 마커의 중심점 (x, y)
|
|
80
|
-
* - 성능: O(1) - 단일 좌표 변환
|
|
81
|
-
*
|
|
82
|
-
* @example
|
|
83
|
-
* ```typescript
|
|
84
|
-
* const offset = computeMarkerOffset(markerData, controller);
|
|
85
|
-
* if (!offset) return; // 변환 실패
|
|
86
|
-
* // offset.x, offset.y는 화면 픽셀 좌표
|
|
87
|
-
* ```
|
|
55
|
+
* @returns 변환된 화면 좌표 또는 null
|
|
88
56
|
*/
|
|
89
57
|
|
|
90
58
|
var computeMarkerOffset = function (markerData, controller) {
|
|
91
|
-
if (!markerData.position)
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
|
|
59
|
+
if (!markerData.position) return null;
|
|
95
60
|
return controller.positionToOffset(markerData.position);
|
|
96
61
|
};
|
|
97
62
|
/**
|
|
98
|
-
* Point-in-Polygon 알고리즘
|
|
99
|
-
*
|
|
100
|
-
* Ray Casting 알고리즘을 사용하여 점이 폴리곤 내부에 있는지 확인합니다.
|
|
63
|
+
* Point-in-Polygon 알고리즘 (Ray Casting)
|
|
101
64
|
*
|
|
102
65
|
* @param point 확인할 점의 좌표
|
|
103
|
-
* @param polygon 폴리곤 좌표 배열
|
|
104
|
-
* @returns 점이 폴리곤 내부에 있으면 true
|
|
105
|
-
*
|
|
106
|
-
* @remarks
|
|
107
|
-
* - **알고리즘**: Ray Casting (Ray Crossing)
|
|
108
|
-
* - **성능**: O(n), n은 폴리곤의 좌표 수
|
|
109
|
-
* - **경계 처리**: 경계선 위의 점은 내부로 간주
|
|
110
|
-
*
|
|
111
|
-
* @example
|
|
112
|
-
* ```typescript
|
|
113
|
-
* const point = { x: 100, y: 200 };
|
|
114
|
-
* const polygon = [[0, 0], [100, 0], [100, 100], [0, 100]];
|
|
115
|
-
* const isInside = isPointInPolygon(point, polygon);
|
|
116
|
-
* ```
|
|
66
|
+
* @param polygon 폴리곤 좌표 배열
|
|
67
|
+
* @returns 점이 폴리곤 내부에 있으면 true
|
|
117
68
|
*/
|
|
118
69
|
|
|
119
70
|
var isPointInPolygon = function (point, polygon) {
|
|
71
|
+
// Ray Casting 알고리즘: 점에서 오른쪽으로 무한히 뻗은 선과 폴리곤 변의 교차 횟수로 판단
|
|
120
72
|
var inside = false;
|
|
121
73
|
|
|
122
74
|
for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
123
75
|
var xi = polygon[i][0],
|
|
124
76
|
yi = polygon[i][1];
|
|
125
77
|
var xj = polygon[j][0],
|
|
126
|
-
yj = polygon[j][1];
|
|
78
|
+
yj = polygon[j][1]; // 점의 y 좌표가 변의 양 끝점 사이에 있고, 교차점의 x 좌표가 점의 x 좌표보다 큰지 확인
|
|
79
|
+
|
|
127
80
|
var intersect = yi > point.y !== yj > point.y && point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi;
|
|
128
|
-
if (intersect) inside = !inside;
|
|
81
|
+
if (intersect) inside = !inside; // 교차할 때마다 inside 상태 토글
|
|
129
82
|
}
|
|
130
83
|
|
|
131
84
|
return inside;
|
|
@@ -133,42 +86,20 @@ var isPointInPolygon = function (point, polygon) {
|
|
|
133
86
|
/**
|
|
134
87
|
* 폴리곤 히트 테스트 (도넛 폴리곤 지원)
|
|
135
88
|
*
|
|
136
|
-
* 점이 폴리곤 내부에 있는지 확인합니다. 도넛 폴리곤(구멍이 있는 폴리곤)을 지원합니다.
|
|
137
|
-
*
|
|
138
89
|
* @param clickedOffset 클릭/마우스 위치 좌표
|
|
139
90
|
* @param polygonData 폴리곤 데이터
|
|
140
91
|
* @param getPolygonOffsets 폴리곤 좌표 변환 함수
|
|
141
|
-
* @returns 점이 폴리곤 내부에 있으면 true
|
|
142
|
-
*
|
|
143
|
-
* @remarks
|
|
144
|
-
* - **도넛 폴리곤 처리** (isDonutPolygon === true):
|
|
145
|
-
* 1. 외부 폴리곤(첫 번째): 내부에 있어야 함
|
|
146
|
-
* 2. 내부 구멍들(나머지): 내부에 있으면 안 됨 (evenodd 규칙)
|
|
147
|
-
* - **일반 폴리곤 처리**: Point-in-Polygon 알고리즘 사용
|
|
148
|
-
* - **성능**: O(n), n은 폴리곤의 총 좌표 수
|
|
149
|
-
*
|
|
150
|
-
* **중요**: 도넛 폴리곤과 내부 폴리곤은 별개의 polygonData로 처리됩니다.
|
|
151
|
-
* - 도넛 폴리곤 A: isDonutPolygon=true
|
|
152
|
-
* - 내부 폴리곤 B: isDonutPolygon=false (별도 데이터)
|
|
153
|
-
*
|
|
154
|
-
* @example
|
|
155
|
-
* ```typescript
|
|
156
|
-
* const isHit = isPointInPolygonData(
|
|
157
|
-
* clickedOffset,
|
|
158
|
-
* polygonData,
|
|
159
|
-
* getOrComputePolygonOffsets
|
|
160
|
-
* );
|
|
161
|
-
* ```
|
|
92
|
+
* @returns 점이 폴리곤 내부에 있으면 true
|
|
162
93
|
*/
|
|
163
94
|
|
|
164
95
|
var isPointInPolygonData = function (clickedOffset, polygonData, getPolygonOffsets) {
|
|
165
96
|
var polygonOffsets = getPolygonOffsets(polygonData);
|
|
166
|
-
if (!polygonOffsets) return false; //
|
|
97
|
+
if (!polygonOffsets) return false; // 도넛 폴리곤 처리: 외부 폴리곤 내부에 있으면서 구멍(hole) 내부에 있지 않아야 함
|
|
167
98
|
|
|
168
99
|
if (polygonData.isDonutPolygon) {
|
|
169
100
|
for (var _i = 0, polygonOffsets_1 = polygonOffsets; _i < polygonOffsets_1.length; _i++) {
|
|
170
101
|
var multiPolygon = polygonOffsets_1[_i];
|
|
171
|
-
if (multiPolygon.length === 0) continue; //
|
|
102
|
+
if (multiPolygon.length === 0) continue; // 구멍이 없는 경우 일반 폴리곤과 동일
|
|
172
103
|
|
|
173
104
|
if (multiPolygon.length === 1) {
|
|
174
105
|
if (isPointInPolygon(clickedOffset, multiPolygon[0])) {
|
|
@@ -176,33 +107,30 @@ var isPointInPolygonData = function (clickedOffset, polygonData, getPolygonOffse
|
|
|
176
107
|
}
|
|
177
108
|
|
|
178
109
|
continue;
|
|
179
|
-
} //
|
|
110
|
+
} // 외부 폴리곤 내부에 있는지 확인
|
|
180
111
|
|
|
181
112
|
|
|
182
113
|
var outerPolygon = multiPolygon[0];
|
|
183
114
|
|
|
184
115
|
if (!isPointInPolygon(clickedOffset, outerPolygon)) {
|
|
185
|
-
continue;
|
|
186
|
-
} //
|
|
116
|
+
continue;
|
|
117
|
+
} // 구멍 내부에 있으면 false (도넛의 빈 공간)
|
|
187
118
|
|
|
188
119
|
|
|
189
120
|
for (var i = 1; i < multiPolygon.length; i++) {
|
|
190
121
|
var hole = multiPolygon[i];
|
|
191
122
|
|
|
192
123
|
if (isPointInPolygon(clickedOffset, hole)) {
|
|
193
|
-
// ❌ 구멍 안에 있음 → 이 도넛 폴리곤은 히트 안 됨
|
|
194
|
-
// 다른 multiPolygon 체크하지 않고 바로 false 반환
|
|
195
|
-
// (도넛 폴리곤의 구멍 안은 무조건 클릭 불가)
|
|
196
124
|
return false;
|
|
197
125
|
}
|
|
198
|
-
} //
|
|
126
|
+
} // 외부 폴리곤 내부에 있으면서 모든 구멍 밖에 있으면 true
|
|
199
127
|
|
|
200
128
|
|
|
201
129
|
return true;
|
|
202
130
|
}
|
|
203
131
|
|
|
204
132
|
return false;
|
|
205
|
-
} // 일반 폴리곤 처리
|
|
133
|
+
} // 일반 폴리곤 처리
|
|
206
134
|
|
|
207
135
|
|
|
208
136
|
for (var _a = 0, polygonOffsets_2 = polygonOffsets; _a < polygonOffsets_2.length; _a++) {
|
|
@@ -221,31 +149,12 @@ var isPointInPolygonData = function (clickedOffset, polygonData, getPolygonOffse
|
|
|
221
149
|
return false;
|
|
222
150
|
};
|
|
223
151
|
/**
|
|
224
|
-
* 마커 히트 테스트 (
|
|
225
|
-
*
|
|
226
|
-
* 점이 마커의 클릭/호버 영역 내부에 있는지 확인합니다.
|
|
227
|
-
* 마커의 꼬리(tail)는 Hit Test 영역에서 제외됩니다.
|
|
152
|
+
* 마커 히트 테스트 (꼬리 제외, 오프셋 지원)
|
|
228
153
|
*
|
|
229
154
|
* @param clickedOffset 클릭/마우스 위치 좌표
|
|
230
155
|
* @param markerData 마커 데이터
|
|
231
156
|
* @param getMarkerOffset 마커 좌표 변환 함수
|
|
232
|
-
* @returns 점이 마커 영역 내부에 있으면 true
|
|
233
|
-
*
|
|
234
|
-
* @remarks
|
|
235
|
-
* - **꼬리 제외**: 꼬리(tail)는 Hit Test 영역에서 제외됩니다
|
|
236
|
-
* - markerOffset.y는 마커 최하단(꼬리 끝) 좌표
|
|
237
|
-
* - boxHeight는 마커 본체만 포함 (꼬리 제외)
|
|
238
|
-
* - tailHeight만큼 위로 올려서 본체만 Hit Test 영역으로 사용
|
|
239
|
-
* - **성능**: O(1) - 단순 사각형 영역 체크
|
|
240
|
-
*
|
|
241
|
-
* @example
|
|
242
|
-
* ```typescript
|
|
243
|
-
* const isHit = isPointInMarkerData(
|
|
244
|
-
* clickedOffset,
|
|
245
|
-
* markerData,
|
|
246
|
-
* getOrComputeMarkerOffset
|
|
247
|
-
* );
|
|
248
|
-
* ```
|
|
157
|
+
* @returns 점이 마커 영역 내부에 있으면 true
|
|
249
158
|
*/
|
|
250
159
|
|
|
251
160
|
var isPointInMarkerData = function (clickedOffset, markerData, getMarkerOffset) {
|
|
@@ -253,58 +162,43 @@ var isPointInMarkerData = function (clickedOffset, markerData, getMarkerOffset)
|
|
|
253
162
|
if (!markerOffset) return false;
|
|
254
163
|
var boxWidth = markerData.boxWidth || 50;
|
|
255
164
|
var boxHeight = markerData.boxHeight || 28;
|
|
256
|
-
var tailHeight = markerData.tailHeight || 0;
|
|
165
|
+
var tailHeight = markerData.tailHeight || 0;
|
|
166
|
+
var offsetX = markerData.offsetX || 0;
|
|
167
|
+
var offsetY = markerData.offsetY || 0; // 오프셋을 적용한 마커 중심점 기준으로 박스 영역 계산 (꼬리는 제외)
|
|
257
168
|
|
|
258
|
-
var x = markerOffset.x - boxWidth / 2;
|
|
259
|
-
var y = markerOffset.y - boxHeight - tailHeight; //
|
|
169
|
+
var x = markerOffset.x + offsetX - boxWidth / 2;
|
|
170
|
+
var y = markerOffset.y + offsetY - boxHeight - tailHeight; // 클릭 위치가 박스 영역 내부에 있는지 확인
|
|
260
171
|
|
|
261
172
|
return clickedOffset.x >= x && clickedOffset.x <= x + boxWidth && clickedOffset.y >= y && clickedOffset.y <= y + boxHeight;
|
|
262
|
-
};
|
|
173
|
+
}; // Hex 색상을 RGBA로 변환
|
|
174
|
+
|
|
263
175
|
var hexToRgba = function (hexColor, alpha) {
|
|
264
176
|
if (alpha === void 0) {
|
|
265
177
|
alpha = 1;
|
|
266
|
-
}
|
|
267
|
-
|
|
178
|
+
}
|
|
268
179
|
|
|
269
|
-
var hex = hexColor.replace('#', '');
|
|
180
|
+
var hex = hexColor.replace('#', '');
|
|
270
181
|
|
|
271
|
-
if (hex.length
|
|
272
|
-
|
|
273
|
-
var g = parseInt(hex.substring(2, 4), 16);
|
|
274
|
-
var b = parseInt(hex.substring(4, 6), 16);
|
|
275
|
-
return "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(alpha, ")");
|
|
182
|
+
if (hex.length !== 6) {
|
|
183
|
+
throw new Error('Invalid hex color format');
|
|
276
184
|
}
|
|
277
185
|
|
|
278
|
-
|
|
186
|
+
var r = parseInt(hex.substring(0, 2), 16);
|
|
187
|
+
var g = parseInt(hex.substring(2, 4), 16);
|
|
188
|
+
var b = parseInt(hex.substring(4, 6), 16);
|
|
189
|
+
return "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(alpha, ")");
|
|
279
190
|
};
|
|
280
191
|
var tempCanvas = document.createElement('canvas');
|
|
281
192
|
var tempCtx = tempCanvas.getContext('2d');
|
|
282
193
|
/**
|
|
283
|
-
* 텍스트
|
|
284
|
-
*
|
|
285
|
-
* Canvas 2D Context의 measureText()를 사용하여 텍스트의 실제 너비를 계산하고,
|
|
286
|
-
* 패딩과 최소 너비를 고려하여 최종 너비를 반환합니다.
|
|
194
|
+
* 텍스트 박스 너비 계산
|
|
287
195
|
*
|
|
288
196
|
* @param params 파라미터 객체
|
|
289
197
|
* @param params.text 측정할 텍스트
|
|
290
|
-
* @param params.fontConfig 폰트 설정
|
|
291
|
-
* @param params.padding
|
|
198
|
+
* @param params.fontConfig 폰트 설정
|
|
199
|
+
* @param params.padding 패딩 값 (px)
|
|
292
200
|
* @param params.minWidth 최소 너비 (px)
|
|
293
|
-
* @returns 계산된 텍스트
|
|
294
|
-
*
|
|
295
|
-
* @remarks
|
|
296
|
-
* - 성능: O(1) - 단일 텍스트 측정
|
|
297
|
-
* - 임시 Canvas를 사용하여 정확한 너비 측정
|
|
298
|
-
*
|
|
299
|
-
* @example
|
|
300
|
-
* ```typescript
|
|
301
|
-
* const width = calculateTextBoxWidth({
|
|
302
|
-
* text: "Hello World",
|
|
303
|
-
* fontConfig: 'bold 16px Arial',
|
|
304
|
-
* padding: 20,
|
|
305
|
-
* minWidth: 60
|
|
306
|
-
* });
|
|
307
|
-
* ```
|
|
201
|
+
* @returns 계산된 텍스트 박스 너비 (px)
|
|
308
202
|
*/
|
|
309
203
|
|
|
310
204
|
var calculateTextBoxWidth = function (_a) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MutableRefObject } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { CanvasData } from "./types";
|
|
3
3
|
/**
|
|
4
4
|
* 뷰포트 영역 (화면에 보이는 영역)
|
|
5
5
|
*/
|
|
@@ -21,20 +21,9 @@ export interface BoundingBox {
|
|
|
21
21
|
/**
|
|
22
22
|
* 현재 뷰포트 영역 계산
|
|
23
23
|
*
|
|
24
|
-
* Konva Stage
|
|
25
|
-
*
|
|
26
|
-
* @param stage Konva Stage 인스턴스 (width, height 메서드 제공)
|
|
24
|
+
* @param stage Konva Stage 인스턴스
|
|
27
25
|
* @param cullingMargin 컬링 여유 공간 (px)
|
|
28
26
|
* @param viewportRef 뷰포트 경계를 저장할 ref
|
|
29
|
-
*
|
|
30
|
-
* @remarks
|
|
31
|
-
* - 화면 밖 cullingMargin만큼의 영역까지 포함하여 계산
|
|
32
|
-
* - 스크롤 시 부드러운 전환을 위해 여유 공간 포함
|
|
33
|
-
*
|
|
34
|
-
* @example
|
|
35
|
-
* ```typescript
|
|
36
|
-
* updateViewport(stageRef.current, cullingMargin, viewportRef);
|
|
37
|
-
* ```
|
|
38
27
|
*/
|
|
39
28
|
export declare const updateViewport: (stage: {
|
|
40
29
|
width: () => number;
|
|
@@ -43,30 +32,11 @@ export declare const updateViewport: (stage: {
|
|
|
43
32
|
/**
|
|
44
33
|
* 아이템이 현재 뷰포트 안에 있는지 확인 (바운딩 박스 캐싱)
|
|
45
34
|
*
|
|
46
|
-
* 뷰포트 컬링을 위한 함수입니다. 바운딩 박스와 뷰포트 경계의 교차를 확인합니다.
|
|
47
|
-
* 바운딩 박스는 캐시되어 성능을 최적화합니다.
|
|
48
|
-
*
|
|
49
35
|
* @template T 마커/폴리곤 데이터의 추가 속성 타입
|
|
50
36
|
* @param item 확인할 아이템
|
|
51
|
-
* @param enableViewportCulling 뷰포트 컬링 활성화 여부
|
|
52
37
|
* @param viewportRef 뷰포트 경계 ref
|
|
53
38
|
* @param boundingBoxCacheRef 바운딩 박스 캐시 ref
|
|
54
39
|
* @param computeBoundingBox 바운딩 박스 계산 함수
|
|
55
|
-
* @returns 뷰포트 안에 있으면 true
|
|
56
|
-
*
|
|
57
|
-
* @remarks
|
|
58
|
-
* - 성능: O(1) (캐시 히트 시) 또는 O(바운딩 박스 계산 비용) (캐시 미스 시)
|
|
59
|
-
* - 바운딩 박스는 자동으로 캐시되어 재사용됨
|
|
60
|
-
*
|
|
61
|
-
* @example
|
|
62
|
-
* ```typescript
|
|
63
|
-
* const isVisible = isInViewport(
|
|
64
|
-
* item,
|
|
65
|
-
* enableViewportCulling,
|
|
66
|
-
* viewportRef,
|
|
67
|
-
* boundingBoxCacheRef,
|
|
68
|
-
* computeBoundingBox
|
|
69
|
-
* );
|
|
70
|
-
* ```
|
|
40
|
+
* @returns 뷰포트 안에 있으면 true
|
|
71
41
|
*/
|
|
72
|
-
export declare const isInViewport: <T>(item:
|
|
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;
|
|
@@ -5,20 +5,9 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
/**
|
|
6
6
|
* 현재 뷰포트 영역 계산
|
|
7
7
|
*
|
|
8
|
-
* Konva Stage
|
|
9
|
-
*
|
|
10
|
-
* @param stage Konva Stage 인스턴스 (width, height 메서드 제공)
|
|
8
|
+
* @param stage Konva Stage 인스턴스
|
|
11
9
|
* @param cullingMargin 컬링 여유 공간 (px)
|
|
12
10
|
* @param viewportRef 뷰포트 경계를 저장할 ref
|
|
13
|
-
*
|
|
14
|
-
* @remarks
|
|
15
|
-
* - 화면 밖 cullingMargin만큼의 영역까지 포함하여 계산
|
|
16
|
-
* - 스크롤 시 부드러운 전환을 위해 여유 공간 포함
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```typescript
|
|
20
|
-
* updateViewport(stageRef.current, cullingMargin, viewportRef);
|
|
21
|
-
* ```
|
|
22
11
|
*/
|
|
23
12
|
var updateViewport = function (stage, cullingMargin, viewportRef) {
|
|
24
13
|
if (!stage) return;
|
|
@@ -32,35 +21,16 @@ var updateViewport = function (stage, cullingMargin, viewportRef) {
|
|
|
32
21
|
/**
|
|
33
22
|
* 아이템이 현재 뷰포트 안에 있는지 확인 (바운딩 박스 캐싱)
|
|
34
23
|
*
|
|
35
|
-
* 뷰포트 컬링을 위한 함수입니다. 바운딩 박스와 뷰포트 경계의 교차를 확인합니다.
|
|
36
|
-
* 바운딩 박스는 캐시되어 성능을 최적화합니다.
|
|
37
|
-
*
|
|
38
24
|
* @template T 마커/폴리곤 데이터의 추가 속성 타입
|
|
39
25
|
* @param item 확인할 아이템
|
|
40
|
-
* @param enableViewportCulling 뷰포트 컬링 활성화 여부
|
|
41
26
|
* @param viewportRef 뷰포트 경계 ref
|
|
42
27
|
* @param boundingBoxCacheRef 바운딩 박스 캐시 ref
|
|
43
28
|
* @param computeBoundingBox 바운딩 박스 계산 함수
|
|
44
|
-
* @returns 뷰포트 안에 있으면 true
|
|
45
|
-
*
|
|
46
|
-
* @remarks
|
|
47
|
-
* - 성능: O(1) (캐시 히트 시) 또는 O(바운딩 박스 계산 비용) (캐시 미스 시)
|
|
48
|
-
* - 바운딩 박스는 자동으로 캐시되어 재사용됨
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* ```typescript
|
|
52
|
-
* const isVisible = isInViewport(
|
|
53
|
-
* item,
|
|
54
|
-
* enableViewportCulling,
|
|
55
|
-
* viewportRef,
|
|
56
|
-
* boundingBoxCacheRef,
|
|
57
|
-
* computeBoundingBox
|
|
58
|
-
* );
|
|
59
|
-
* ```
|
|
29
|
+
* @returns 뷰포트 안에 있으면 true
|
|
60
30
|
*/
|
|
61
31
|
|
|
62
|
-
var isInViewport = function (item,
|
|
63
|
-
if (!
|
|
32
|
+
var isInViewport = function (item, viewportRef, boundingBoxCacheRef, computeBoundingBox) {
|
|
33
|
+
if (!viewportRef.current) return true;
|
|
64
34
|
var viewport = viewportRef.current; // 캐시된 바운딩 박스 확인
|
|
65
35
|
|
|
66
36
|
var bbox = boundingBoxCacheRef.current.get(item.id);
|