@rocon/balcan 0.1.1 → 1.0.0

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.
@@ -1,5 +1,9 @@
1
+ import { range } from 'lodash';
2
+ import Matrix from './ts-matrix/Matrix';
3
+ /** 일반 수학과 기하학 관련 계산 함수들을 모아둔 네임스페이스 */
1
4
  export var math;
2
5
  (function (math) {
6
+ /** 주어진 숫자들의 평균을 구한다 */
3
7
  function average(...values) {
4
8
  if (values.length === 0)
5
9
  return 0;
@@ -74,12 +78,45 @@ export var math;
74
78
  return r;
75
79
  }
76
80
  math.vectorAdd = vectorAdd;
81
+ /** 벡터 뺄셈 */
82
+ function vectorSubtract(v1, v2) {
83
+ const r = { x: v1.x - v2.x, y: v1.y - v2.y };
84
+ if ('theta' in v1 && 'theta' in v2)
85
+ return { ...r, theta: v1.theta - v2.theta };
86
+ else
87
+ return r;
88
+ }
89
+ math.vectorSubtract = vectorSubtract;
90
+ /** 벡터 곱셈 */
91
+ function vectorMultiply(v1, scalar) {
92
+ const r = {
93
+ ...v1,
94
+ x: v1.x * scalar,
95
+ y: v1.y * scalar,
96
+ };
97
+ return r;
98
+ }
99
+ math.vectorMultiply = vectorMultiply;
100
+ /** 벡터 나눗셈 */
101
+ function vectorDivide(v1, scalar) {
102
+ if (scalar === 0) {
103
+ console.error('vectorDivide: divide by zero');
104
+ return v1;
105
+ }
106
+ const r = {
107
+ ...v1,
108
+ x: v1.x / scalar,
109
+ y: v1.y / scalar,
110
+ };
111
+ return r;
112
+ }
113
+ math.vectorDivide = vectorDivide;
77
114
  /** 벡터 내적 */
78
115
  function vectorDot(v1, v2) {
79
116
  return v1.x * v2.x + v1.y * v2.y;
80
117
  }
81
118
  math.vectorDot = vectorDot;
82
- /** position like 자료형을 vector2d 로 변환함 */
119
+ /** 좌표형 자료형 (Position, Pose) 를 vector2d 로 변환함 */
83
120
  function vector2d(point) {
84
121
  if ('stagex' in point)
85
122
  return { x: point.stagex, y: point.stagey };
@@ -87,4 +124,181 @@ export var math;
87
124
  return { x: point.x, y: point.y };
88
125
  }
89
126
  math.vector2d = vector2d;
127
+ /** 벡터들 에 회전 행렬 적용 */
128
+ function rotateVectors(params) {
129
+ const { x: centerX = 0, y: centerY = 0 } = params?.centerPosition ?? {
130
+ x: 0,
131
+ y: 0,
132
+ };
133
+ const rad = params?.angleUnit === 'degree'
134
+ ? math.radian(params.rotateAngle, { invertY: params?.invertY })
135
+ : params.rotateAngle;
136
+ const rotationMatrix = new Matrix(2, 2, [
137
+ [Math.cos(rad), -Math.sin(rad)],
138
+ [Math.sin(rad), Math.cos(rad)],
139
+ ]);
140
+ // 회전중심점과 회전할 점의 거리를 벡터로 표현
141
+ const vectorMatrix = new Matrix(2, params.points.length, [
142
+ params.points.map((p) => p.x - centerX),
143
+ params.points.map((p) => p.y - centerY),
144
+ ]);
145
+ const rotatedMatrix = rotationMatrix.multiply(vectorMatrix);
146
+ const results = range(rotatedMatrix.columns).map((m) => {
147
+ return {
148
+ x: rotatedMatrix.values[0][m] + centerX,
149
+ y: rotatedMatrix.values[1][m] + centerY,
150
+ };
151
+ });
152
+ return results;
153
+ }
154
+ math.rotateVectors = rotateVectors;
155
+ /**
156
+ * 어떤 점 P와 어떤 선분 S가 있다고 할 때
157
+ * 선분 S 위에 존재할 수 있는 무수한 점들 중 - P에서 가장 가까운 점 X를 찾는다
158
+ * @param point 점 위치벡터 P
159
+ * @param segmentPosition 선분 S의 정의 (양 끝점의 좌표)
160
+ * @returns X점의 좌표 벡터
161
+ */
162
+ function nearestPointOnSegment(point, segmentPosition) {
163
+ /** 선분 출발점 */
164
+ const segmentStartPoint = segmentPosition[0];
165
+ const segmentEndPoint = segmentPosition[1];
166
+ /** 선분의 방향벡터 */
167
+ const SegmentVector = {
168
+ x: segmentEndPoint.x - segmentStartPoint.x,
169
+ y: segmentEndPoint.y - segmentStartPoint.y,
170
+ };
171
+ /** 점에서 선분 출발점으로의 벡터 */
172
+ const AP = {
173
+ x: point.x - segmentStartPoint.x,
174
+ y: point.y - segmentStartPoint.y,
175
+ };
176
+ /** 점에서 선분 종점으로의 벡터 */
177
+ const BP = {
178
+ x: point.x - segmentEndPoint.x,
179
+ y: point.y - segmentEndPoint.y,
180
+ };
181
+ /** 선분 방향벡터와 점에서 선분 출발점으로의 벡터의 내적 */
182
+ const dot_AB_AP = SegmentVector.x * AP.x + SegmentVector.y * AP.y;
183
+ /** 선분 방향벡터와 점에서 선분 종점으로의 벡터의 내적 */
184
+ const dot_BA_AP = -SegmentVector.x * BP.x - SegmentVector.y * BP.y;
185
+ // dot_AB_AP + dot_BA_AP = |B - A|² 이므로, 0이면 선분의 길이가 0 (점으로 퇴화)
186
+ // 부동소수점 연산 오차를 고려하여 매우 작은 값(epsilon) 이하인 경우도 처리
187
+ const epsilon = 1e-10;
188
+ const denominator = dot_AB_AP + dot_BA_AP;
189
+ if (Math.abs(denominator) < epsilon) {
190
+ // 선분이 너무 짧아 점으로 퇴화된 경우, 시작점을 정답으로 반환
191
+ // 그렇지 않을 경우 const t 를 계산할떄 divide by zero 에러가 발생할 수 있음
192
+ return segmentStartPoint;
193
+ }
194
+ if (dot_AB_AP <= 0)
195
+ return segmentStartPoint;
196
+ else if (dot_BA_AP <= 0)
197
+ return segmentPosition[1];
198
+ else {
199
+ const t = dot_AB_AP / denominator;
200
+ return {
201
+ x: segmentStartPoint.x + SegmentVector.x * t,
202
+ y: segmentStartPoint.y + SegmentVector.y * t,
203
+ };
204
+ }
205
+ }
206
+ math.nearestPointOnSegment = nearestPointOnSegment;
207
+ /**
208
+ * 주어진 점이 주어진 다각형 내부에 있는지 확인
209
+ * @param point 점의 좌표
210
+ * @param polygon 다각형의 정의 (점들의 좌표)
211
+ * @returns 점이 다각형 내부에 있는지 여부
212
+ */
213
+ function isPointInPolygon(point, polygon) {
214
+ let crosses = 0;
215
+ for (const _i in polygon) {
216
+ const i = Number(_i);
217
+ const j = (i + 1) % polygon.length;
218
+ const p1 = polygon[i];
219
+ const p2 = polygon[j];
220
+ // NOTE: p1.y 와 p2.y 가 같은 값이면 if 조건은 만족할 수가 없다
221
+ // 따라서 if 내에서 p2.y - p1.y 가 0이 되는 경우는 없으므로 divide by zero 에러는 발생할 수 없다
222
+ if (p1.y > point.y !== p2.y > point.y) {
223
+ const atx = ((p2.x - p1.x) * (point.y - p1.y)) / (p2.y - p1.y) + p1.x;
224
+ if (point.x < atx)
225
+ crosses += 1;
226
+ }
227
+ }
228
+ return crosses % 2 === 1;
229
+ }
230
+ math.isPointInPolygon = isPointInPolygon;
231
+ /**
232
+ * originPoint 와 endPoint 를 잇는 선분을 가정하고
233
+ * extendLength 만큼 양쪽으로 늘이거나 줄인 선분을 반환
234
+ * (extendLength는 음수일 수도 있음)
235
+ */
236
+ function extendSegment(params) {
237
+ const { originPoint, endPoint, extendLengthFromOrigin, extendLengthFromEnd, } = params;
238
+ const dx = endPoint.x - originPoint.x;
239
+ const dy = endPoint.y - originPoint.y;
240
+ const length = Math.sqrt(dx * dx + dy * dy);
241
+ // 길이가 0인 경우 그대로 반환
242
+ if (length === 0) {
243
+ return { origin: { ...originPoint }, end: { ...endPoint } };
244
+ }
245
+ // 단위벡터 (방향 유지, 음수 extendLength면 짧아짐)
246
+ const ux = dx / length;
247
+ const uy = dy / length;
248
+ // 확장된 점 계산 (extendLength가 음수라면 줄어듦)
249
+ const newOrigin = {
250
+ x: originPoint.x - ux * extendLengthFromOrigin,
251
+ y: originPoint.y - uy * extendLengthFromOrigin,
252
+ };
253
+ const newEnd = {
254
+ x: endPoint.x + ux * extendLengthFromEnd,
255
+ y: endPoint.y + uy * extendLengthFromEnd,
256
+ };
257
+ return { origin: newOrigin, end: newEnd };
258
+ }
259
+ math.extendSegment = extendSegment;
260
+ /**
261
+ * 실좌표계 상 위치 (Pose) 를 화면좌표계로 변환
262
+ * @param pose 실좌표계 상의 위치
263
+ * @param origin 원점을 삼으려는 화면좌표계 상 좌표
264
+ * @param pixelPerMeter 1미터당 픽셀 수
265
+ */
266
+ function transformPose(pose, origin, pixelPerMeter) {
267
+ if (pixelPerMeter === 0) {
268
+ console.error('transformPosition: pixelPerMeter must be greater than 0');
269
+ return {
270
+ stagex: 0,
271
+ stagey: 0,
272
+ degree: 0,
273
+ };
274
+ }
275
+ return {
276
+ stagex: origin.x + pose.x * pixelPerMeter,
277
+ stagey: origin.y - pose.y * pixelPerMeter,
278
+ degree: math.degree(pose.theta, { invertY: false }),
279
+ };
280
+ }
281
+ math.transformPose = transformPose;
282
+ /**
283
+ * 화면좌표계상 위치를 실좌표계로 변환
284
+ * @param position
285
+ * @param realOrigin 원점을 삼으려는 화면좌표계 상 좌표
286
+ * @param pixelPerMeter 1미터당 픽셀 수
287
+ */
288
+ function transformPosition(position, realOrigin, pixelPerMeter) {
289
+ if (pixelPerMeter === 0) {
290
+ console.error('transformPosition: pixelPerMeter must be greater than 0');
291
+ return {
292
+ x: 0,
293
+ y: 0,
294
+ theta: 0,
295
+ };
296
+ }
297
+ return {
298
+ x: (position.stagex - realOrigin.x) / pixelPerMeter,
299
+ y: ((position.stagey - realOrigin.y) / pixelPerMeter) * -1,
300
+ theta: math.radian(position.degree, { invertY: false }),
301
+ };
302
+ }
303
+ math.transformPosition = transformPosition;
90
304
  })(math || (math = {}));
@@ -1,7 +1,9 @@
1
+ import type Konva from 'konva';
1
2
  import type { Director as DirectorClass } from './core.balcan';
2
3
  export type Director = DirectorClass;
4
+ export type BalcanStage = Omit<Konva.Stage, 'scale' | 'scaleX' | 'scaleY'>;
3
5
  /**
4
- * Staff Layer, Canvas 에서 작동하는 특정한 목적을 지닌 최소 로직 집합이다.
6
+ * Staff, Canvas 에서 작동하는 특정한 목적을 지닌 최소 로직 집합이다.
5
7
  * 단, 비지니스 로직을 포함하지 않는 포괄적인 기능을 수행해야 한다.
6
8
  *
7
9
  * @template TYPE 종류를 나타내는 문자열, type 의 값을 리터럴 형식으로 고정하기 위해 쓰임
@@ -27,13 +29,16 @@ export type Staff<TYPE extends string = any, TData = any, TReturnExtend extends
27
29
  addPlugin?: (plugin: Plugin) => void;
28
30
  /** 플러그인 제거 */
29
31
  removePlugin?: (pluginKey: string) => void;
32
+ /** staff 가 파괴될때 수행할 로직을 정의할 수 있음 */
30
33
  onDestroy?: () => void;
31
- onBeforeRender?: () => void;
32
- onRender?: () => void;
33
34
  } & TReturnExtend;
34
35
  /**
35
- * Actor Layer, 유닛을 조합하여 특정한 목적을 수행하는 로직 집합이다.
36
+ * Actor, 유닛을 조합하여 특정한 목적을 수행하는 로직 집합이다.
36
37
  * 비지니스 로직을 포함 할 수 있다.
38
+ *
39
+ * @template TYPE 종류를 나타내는 문자열, type 의 값을 리터럴 형식으로 고정하기 위해 쓰임
40
+ * @template TData 자체적으로 가지는 추가 데이터 타입, `data` 프로퍼티의 자료형으로 쓰임
41
+ * @template TReturnExtend 현재 타입을 TReturnExtend 만큼 확장할 수 있음
37
42
  */
38
43
  export type Actor<TYPE extends string = any, TData = any, TReturnExtend extends {
39
44
  [key: string]: unknown;
@@ -52,13 +57,14 @@ export type Actor<TYPE extends string = any, TData = any, TReturnExtend extends
52
57
  addPlugin?: (plugin: Plugin) => void;
53
58
  removePlugin?: (pluginKey: string) => void;
54
59
  onDestroy?: () => void;
55
- onBeforeRender?: () => void;
56
- onRender?: () => void;
57
- onUpdate?: () => void;
58
60
  } & TReturnExtend;
59
61
  /**
60
- * Scene Layer
62
+ * Scene
61
63
  * 각 화면의 요구사항이 구현된 로직 집합체
64
+ *
65
+ * @template TYPE 종류를 나타내는 문자열, type 의 값을 리터럴 형식으로 고정하기 위해 쓰임
66
+ * @template TData 자체적으로 가지는 추가 데이터 타입, `data` 프로퍼티의 자료형으로 쓰임
67
+ * @template TReturnExtend 현재 타입을 TReturnExtend 만큼 확장할 수 있음
62
68
  */
63
69
  export type Scene<TYPE extends string = any, TData = any, TReturnExtend extends {
64
70
  [key: string]: unknown;
@@ -71,19 +77,20 @@ export type Scene<TYPE extends string = any, TData = any, TReturnExtend extends
71
77
  data: TData;
72
78
  /** 앱이 소속되는 스테이지 객체 */
73
79
  director: Director;
80
+ onDestroy: () => void;
74
81
  } & TReturnExtend;
75
82
  /**
76
83
  * 플러그인 정의, 실제 사용할 때는 확장해서 사용 권장
77
- * @template T 플러그인이 자체적으로 가지는 추가 데이터 타입
84
+ *
85
+ * @template TYPE 종류를 나타내는 문자열, type 의 값을 리터럴 형식으로 고정하기 위해 쓰임
86
+ * @template TData 자체적으로 가지는 추가 데이터 타입, `data` 프로퍼티의 자료형으로 쓰임
78
87
  */
79
88
  export interface Plugin<TYPE extends string = any, TData = any> {
80
89
  type: TYPE;
81
90
  /** 스테이지 위에 추가되는 plugin 은 다른 plugin 과 배타적인 유일값을 가져야 함 */
82
91
  uniqueId: string;
83
92
  data: TData;
84
- onBeforeRender?: () => void;
85
- onRender?: () => void;
86
- onUpdate?: (newData: TData) => void;
93
+ onUpdate: (newData: TData) => void;
87
94
  }
88
95
  export type onTransformCallback = {
89
96
  x: number;
@@ -1,23 +1,28 @@
1
+ import type { BalcanTypes } from '..';
1
2
  import type Konva from 'konva';
3
+ import type { KonvaEventObject } from 'konva/lib/Node';
4
+ import type { BalcanStage } from './types.balcan';
2
5
  export declare namespace util {
3
6
  /**
4
7
  * Konva Shape 나 Group에 마우스가 올라갔을 때 커서 모양을 바꿔주는 이벤트를 등록한다
5
8
  * @param shapes 등록할 Konva Shape 들
6
9
  */
7
10
  function enableMouseHoverEvent(params: {
8
- shapes: (Konva.Shape | Konva.Group)[];
11
+ shapes: Konva.Node[];
9
12
  cursorStyle?: string;
10
- additionalHoverEvent?: (shape: Konva.Shape | Konva.Group) => void;
11
- additionalLeaveEvent?: (shape: Konva.Shape | Konva.Group) => void;
13
+ additionalHoverEvent?: (shape: Konva.Node) => void;
14
+ additionalLeaveEvent?: (shape: Konva.Node) => void;
12
15
  }): void;
13
- function changeMouseCursor(stage: Konva.Stage, cursorStyle: string): void;
14
16
  /** Konva Shape 나 Group에 걸려있는 모든 이벤트를 제거한다 */
15
- function disableAllEvents(...shapes: (Konva.Shape | Konva.Group)[]): void;
16
- function uuid(): string;
17
- function pickShape(stage: Konva.Stage): import("konva/lib/Shape").Shape<import("konva/lib/Shape").ShapeConfig> | null;
17
+ function disableAllEvents(...shapes: Konva.Node[]): void;
18
+ /** 마우스 위치에 있는 첫 번째 shape 를 가져온다 */
19
+ function pickShape(stage: BalcanStage): import("konva/lib/Shape").Shape<import("konva/lib/Shape").ShapeConfig> | null;
18
20
  /** 마우스 위치에 있는 모든 shape 목록을 가져온다 */
19
- function pickShapes(stage: Konva.Stage): import("konva/lib/Shape").Shape<import("konva/lib/Shape").ShapeConfig>[];
21
+ function pickShapes(stage: BalcanStage): import("konva/lib/Shape").Shape<import("konva/lib/Shape").ShapeConfig>[];
20
22
  /** 현재 마우스포인터의 pose + position 계산 */
21
- function getPointerPosePosition(stage: Konva.Stage, originPoint: BalcanGeo.Vector2 | BalcanGeo.Pose, pixelPerMeter: number): BalcanGeo.PosePosition;
23
+ function getPointerPosePosition(director: BalcanTypes.Director, originPoint: BalcanGeo.Vector2 | BalcanGeo.Pose, pixelPerMeter: number): BalcanGeo.PosePosition;
24
+ /** 랜덤 16진수 색상 코드를 생성한다 */
22
25
  function randomHexColorCode(): string;
26
+ function doubleClickEvent(shape: Konva.Node, onDoubleClicked: (e: KonvaEventObject<MouseEvent, Konva.Node>) => void): void;
27
+ function stageDoubleClickEvent(stage: Konva.Stage, onDoubleClicked: (e: KonvaEventObject<MouseEvent, Konva.Stage>) => void): void;
23
28
  }
@@ -1,4 +1,4 @@
1
- import { geo } from './geo.balcan';
1
+ import { math } from './math.balcan';
2
2
  export var util;
3
3
  (function (util) {
4
4
  /**
@@ -18,19 +18,12 @@ export var util;
18
18
  });
19
19
  }
20
20
  util.enableMouseHoverEvent = enableMouseHoverEvent;
21
- function changeMouseCursor(stage, cursorStyle) {
22
- stage.container().style.cursor = cursorStyle;
23
- }
24
- util.changeMouseCursor = changeMouseCursor;
25
21
  /** Konva Shape 나 Group에 걸려있는 모든 이벤트를 제거한다 */
26
22
  function disableAllEvents(...shapes) {
27
23
  shapes.forEach((shape) => shape.off('mouseover mouseleave mouseout dragmove transform click dblclick mouseup mousedown'));
28
24
  }
29
25
  util.disableAllEvents = disableAllEvents;
30
- function uuid() {
31
- return Math.random().toString(16).slice(2);
32
- }
33
- util.uuid = uuid;
26
+ /** 마우스 위치에 있는 첫 번째 shape 를 가져온다 */
34
27
  function pickShape(stage) {
35
28
  const picked = stage.getIntersection(stage.getPointerPosition() ?? {
36
29
  x: 0,
@@ -49,23 +42,51 @@ export var util;
49
42
  }
50
43
  util.pickShapes = pickShapes;
51
44
  /** 현재 마우스포인터의 pose + position 계산 */
52
- function getPointerPosePosition(stage, originPoint, pixelPerMeter) {
53
- const pointer = stage.getRelativePointerPosition();
45
+ function getPointerPosePosition(director, originPoint, pixelPerMeter) {
46
+ if (!director.isPointerOverStage)
47
+ return {
48
+ pose: { x: 0, y: 0, theta: 0 },
49
+ position: { stagex: 0, stagey: 0, degree: 0 },
50
+ };
51
+ const pointer = director.stage.getRelativePointerPosition();
54
52
  const position = {
55
53
  stagex: pointer?.x ?? 0,
56
54
  stagey: pointer?.y ?? 0,
57
55
  degree: 0,
58
56
  };
59
- const pose = geo.transformPosition(position, originPoint, pixelPerMeter);
57
+ const pose = math.transformPosition(position, originPoint, pixelPerMeter);
60
58
  return {
61
59
  pose,
62
60
  position,
63
61
  };
64
62
  }
65
63
  util.getPointerPosePosition = getPointerPosePosition;
64
+ /** 랜덤 16진수 색상 코드를 생성한다 */
66
65
  function randomHexColorCode() {
67
66
  return ('#' +
68
67
  ('00000' + Math.floor(Math.random() * Math.pow(16, 6)).toString(16)).slice(-6));
69
68
  }
70
69
  util.randomHexColorCode = randomHexColorCode;
70
+ function doubleClickEvent(shape, onDoubleClicked) {
71
+ shape.on('dblclick', innerMainFunc);
72
+ function innerMainFunc(e) {
73
+ if (e.evt.button !== 0)
74
+ return;
75
+ onDoubleClicked(e);
76
+ shape.off('dblclick');
77
+ setTimeout(() => doubleClickEvent(shape, onDoubleClicked), 400);
78
+ }
79
+ }
80
+ util.doubleClickEvent = doubleClickEvent;
81
+ function stageDoubleClickEvent(stage, onDoubleClicked) {
82
+ stage.on('dblclick', innerMainFunc);
83
+ function innerMainFunc(e) {
84
+ if (e.evt.button !== 0)
85
+ return;
86
+ onDoubleClicked(e);
87
+ stage.off('dblclick');
88
+ setTimeout(() => stageDoubleClickEvent(stage, onDoubleClicked), 400);
89
+ }
90
+ }
91
+ util.stageDoubleClickEvent = stageDoubleClickEvent;
71
92
  })(util || (util = {}));
package/dist/index.d.ts CHANGED
@@ -1,13 +1,5 @@
1
1
  /// <reference path="./types/index.d.ts" />
2
2
  import * as balcan from './mixin/mixin';
3
3
  import type * as BalcanTypes from './mixin/mixin.type';
4
- export declare const Balcan: {
5
- core: typeof balcan.core;
6
- staffs: typeof balcan.staffs;
7
- geo: typeof balcan.geo;
8
- math: typeof balcan.math;
9
- util: typeof balcan.util;
10
- viewport: typeof balcan.viewport;
11
- bitmap: typeof balcan.bitmap;
12
- };
4
+ export declare const Balcan: typeof balcan;
13
5
  export type { BalcanTypes };
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /// <reference path="./types/index.d.ts" />
2
2
  import * as balcan from './mixin/mixin';
3
- export const Balcan = { ...balcan };
3
+ export const Balcan = balcan;
@@ -1,7 +1,5 @@
1
1
  export * as core from '../core/core.balcan';
2
2
  export * as staffs from '../staffs';
3
- export * from '../core/geo.balcan';
4
3
  export * from '../core/math.balcan';
5
4
  export * from '../core/util.balcan';
6
- export * from '../core/viewport.balcan';
7
5
  export * from '../core/bitmap.balcan';
@@ -1,7 +1,5 @@
1
1
  export * as core from '../core/core.balcan';
2
2
  export * as staffs from '../staffs';
3
- export * from '../core/geo.balcan';
4
3
  export * from '../core/math.balcan';
5
4
  export * from '../core/util.balcan';
6
- export * from '../core/viewport.balcan';
7
5
  export * from '../core/bitmap.balcan';
@@ -1,3 +1,3 @@
1
1
  export * from './wheelZoom.staff';
2
2
  export * from './image.staff';
3
- export * from './windowResizeObserver.staff';
3
+ export * from './resizeObserver.staff';
@@ -1,3 +1,3 @@
1
1
  export * from './wheelZoom.staff';
2
2
  export * from './image.staff';
3
- export * from './windowResizeObserver.staff';
3
+ export * from './resizeObserver.staff';
@@ -0,0 +1,14 @@
1
+ import type { Staff, Director } from '../core/types.balcan';
2
+ export type BalcanResizeObserverStaff = Staff<'resizeobserver'>;
3
+ /**
4
+ * Canvas 의 크기가 변경되었을 때 자동으로 konva stage 의 크기를 변경해줍니다.
5
+ * 이 함수를 한번 부르기만 하면 로직이 적용 됩니다.
6
+ */
7
+ export declare function useResizeObserverStaff(opt: {
8
+ /** 임의로 고유 id 를 넣을 수 있음 */
9
+ uuid?: string;
10
+ /** 이 staff 를 소속시킬 Balcan Director */
11
+ director: Director;
12
+ /** 리사이즈 감지되었을 때 추가로 실행하고 싶은 함수 */
13
+ onResizeFunction?: () => void;
14
+ }): BalcanResizeObserverStaff;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Canvas 의 크기가 변경되었을 때 자동으로 konva stage 의 크기를 변경해줍니다.
3
+ * 이 함수를 한번 부르기만 하면 로직이 적용 됩니다.
4
+ */
5
+ export function useResizeObserverStaff(opt) {
6
+ const container = document.querySelector(opt.director.name);
7
+ const observer = new ResizeObserver(resizeHandler);
8
+ observer.observe(container);
9
+ function resizeHandler() {
10
+ opt.director.stage.width(container.clientWidth);
11
+ opt.director.stage.height(container.clientHeight);
12
+ opt.onResizeFunction?.();
13
+ }
14
+ opt.onResizeFunction?.();
15
+ const me = {
16
+ type: 'resizeobserver',
17
+ director: opt.director,
18
+ entityId: opt.uuid ?? '',
19
+ uniqueId: 'resizeobserver' + (opt.uuid ? `-${opt.uuid}` : ''),
20
+ plugins: [],
21
+ data: {},
22
+ onDestroy() {
23
+ observer.disconnect();
24
+ },
25
+ };
26
+ opt.director.addStaff(me);
27
+ return me;
28
+ }
@@ -2,9 +2,13 @@ import type { Director } from '../core/types.balcan';
2
2
  export type BalcanZoomStaff = ReturnType<typeof useWheelZoomStaff>;
3
3
  /** 마우스 휠로 줌 인/아웃을 조작할 수 있는 Staff */
4
4
  export declare function useWheelZoomStaff(opt: {
5
+ /** 임의로 고유 id 를 넣을 수 있음 */
5
6
  uuid: string;
7
+ /** 이 staff 를 소속시킬 Balcan Director */
6
8
  director: Director;
9
+ /** 최소 줌 인 값, 기본값은 0.2 */
7
10
  minScale?: number;
11
+ /** 최대 줌 인 값, 기본값은 3 */
8
12
  maxScale?: number;
9
13
  }): {
10
14
  type: "zoom";
@@ -16,6 +20,4 @@ export declare function useWheelZoomStaff(opt: {
16
20
  addPlugin?: (plugin: import("../core/types.balcan").Plugin) => void;
17
21
  removePlugin?: (pluginKey: string) => void;
18
22
  onDestroy?: () => void;
19
- onBeforeRender?: () => void;
20
- onRender?: () => void;
21
23
  };
@@ -9,17 +9,17 @@ export function useWheelZoomStaff(opt) {
9
9
  plugins: [],
10
10
  data: {},
11
11
  onDestroy() {
12
- director.stage.off('wheel');
12
+ director.stage.off('wheel', handleWheel);
13
13
  },
14
14
  };
15
- director.stage.on('wheel', (e) => {
15
+ director.stage.on('wheel', handleWheel);
16
+ function handleWheel(e) {
16
17
  e.evt.preventDefault();
17
18
  const exponentedScale = Math.min(30, Math.E ** (director.scale / 2));
18
19
  const delta = -e.evt.deltaY * 0.0002 * exponentedScale;
19
20
  const addedScale = director.scale + delta;
20
21
  _setScale(addedScale, true);
21
- });
22
- /** 현재 마우스 포인터 위치를 기준으로 줌 인/아웃을 설정합니다 */
22
+ }
23
23
  function _setScale(newScale, withPointer = false) {
24
24
  const stage = director.stage;
25
25
  const sanitizedScale = Math.min(maxScale, Math.max(minScale, newScale));
@@ -49,9 +49,6 @@ declare namespace BalcanGeo {
49
49
  /** 실좌표계의 사격형 넓이와 중심점 위치를 표현 */
50
50
  type PoseArea = Pose & AreaMeter;
51
51
 
52
- /** 사각형의 넓이와 회전값만을 표현 (위치는 없음) */
53
- type PoseDegreeArea = PoseDegree & AreaMeter;
54
-
55
52
  /** 픽셀 단위계의 사격형 넓이 */
56
53
  type AreaPixel = {
57
54
  widthPx: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rocon/balcan",
3
- "version": "0.1.1",
3
+ "version": "1.0.0",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "dev": "tsc -p tsconfig.json --watch --noEmit",
@@ -9,9 +9,6 @@
9
9
  },
10
10
  "author": "",
11
11
  "license": "ISC",
12
- "dependencies": {
13
- "lodash": "^4.17.21"
14
- },
15
12
  "devDependencies": {
16
13
  "@eslint/js": "^9.26.0",
17
14
  "@types/lodash": "^4.17.16",
@@ -25,10 +22,11 @@
25
22
  "globals": "^16.1.0",
26
23
  "prettier": "^3.3.3",
27
24
  "typescript": "~5.8.3",
28
- "typescript-eslint": "^8.32.1"
25
+ "typescript-eslint": "^8.32.1",
26
+ "lodash": "^4.17.21"
29
27
  },
30
28
  "peerDependencies": {
31
- "konva": "^9.3.22"
29
+ "konva": "^10.2.5"
32
30
  },
33
31
  "main": "dist/index.js",
34
32
  "types": "dist/index.d.ts",