@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.
- package/dist/core/bitmap.balcan.d.ts +1 -0
- package/dist/core/bitmap.balcan.js +1 -0
- package/dist/core/core.balcan.d.ts +121 -20
- package/dist/core/core.balcan.js +235 -54
- package/dist/core/math.balcan.d.ts +68 -1
- package/dist/core/math.balcan.js +215 -1
- package/dist/core/types.balcan.d.ts +19 -12
- package/dist/core/util.balcan.d.ts +14 -9
- package/dist/core/util.balcan.js +33 -12
- package/dist/index.d.ts +1 -9
- package/dist/index.js +1 -1
- package/dist/mixin/mixin.d.ts +0 -2
- package/dist/mixin/mixin.js +0 -2
- package/dist/staffs/index.d.ts +1 -1
- package/dist/staffs/index.js +1 -1
- package/dist/staffs/resizeObserver.staff.d.ts +14 -0
- package/dist/staffs/resizeObserver.staff.js +28 -0
- package/dist/staffs/wheelZoom.staff.d.ts +4 -2
- package/dist/staffs/wheelZoom.staff.js +4 -4
- package/dist/types/geometry.d.ts +0 -3
- package/package.json +4 -6
- package/dist/core/geo.balcan.d.ts +0 -109
- package/dist/core/geo.balcan.js +0 -255
- package/dist/core/viewport.balcan.d.ts +0 -27
- package/dist/core/viewport.balcan.js +0 -60
- package/dist/staffs/windowResizeObserver.staff.d.ts +0 -8
- package/dist/staffs/windowResizeObserver.staff.js +0 -17
|
@@ -1,40 +1,78 @@
|
|
|
1
|
-
import type Konva from 'konva';
|
|
2
1
|
import type { KonvaEventObject } from 'konva/lib/Node';
|
|
2
|
+
import Konva from 'konva';
|
|
3
3
|
import { Subject } from 'rxjs';
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
import type { Actor, BalcanStage, Scene, Staff } from './types.balcan';
|
|
5
|
+
/**
|
|
6
|
+
* 새 Director 를 생성
|
|
7
|
+
* @param divQuerySelector Konva.Stage 가 생성될 div 의 querySelector
|
|
8
|
+
*/
|
|
9
|
+
export declare function createDirector(divQuerySelector: string): Director | undefined;
|
|
10
|
+
export declare function getDirector(divQuerySelector: string): Director | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* 기존 Director 를 회수합니다
|
|
13
|
+
* @param divQuerySelector Director 가 생성된 div 의 querySelector
|
|
14
|
+
*/
|
|
15
|
+
export declare function destroyDirector(divQuerySelector: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* 디렉터는 하나의 Konva.Stage 와 그 위에 올라올 수 있는 Balcan 요소들을 Containing 합니다.
|
|
18
|
+
* Konva.Stage 는 보통 하나의 div 에 매칭되기 때문에 Director 역시 하나의 Div와 매칭된다고 봐도 좋습니다.
|
|
19
|
+
*/
|
|
11
20
|
export declare class Director {
|
|
12
21
|
readonly name: string;
|
|
13
|
-
readonly
|
|
14
|
-
|
|
22
|
+
readonly _stage: Konva.Stage;
|
|
23
|
+
private _isPointerOverStage;
|
|
24
|
+
readonly scenes: {
|
|
15
25
|
[k: string]: Scene;
|
|
16
26
|
};
|
|
17
|
-
actors: {
|
|
27
|
+
readonly actors: {
|
|
18
28
|
[k: string]: Actor;
|
|
19
29
|
};
|
|
20
|
-
staffs: {
|
|
30
|
+
readonly staffs: {
|
|
21
31
|
[k: string]: Staff;
|
|
22
32
|
};
|
|
23
33
|
constructor(name: string, stage: Konva.Stage);
|
|
24
|
-
|
|
34
|
+
/** 내부의 모든 자원을 회수합니다 */
|
|
35
|
+
dispose(): void;
|
|
36
|
+
private _clickHandler;
|
|
37
|
+
/**
|
|
38
|
+
* 이 클래스의 set scale 을 사용하여 스케일 변경시 발생하는 이벤트
|
|
39
|
+
*/
|
|
25
40
|
onScaleChanged$: Subject<{
|
|
26
41
|
scale: number;
|
|
27
42
|
}>;
|
|
28
|
-
|
|
43
|
+
/**
|
|
44
|
+
* stage 에서 더블 클릭 이벤트
|
|
45
|
+
*/
|
|
46
|
+
onMouseDoubleClicked$: Subject<KonvaEventObject<MouseEvent, BalcanStage>>;
|
|
47
|
+
/**
|
|
48
|
+
* stage 에서 키보드 down 이벤트
|
|
49
|
+
*/
|
|
29
50
|
onKeyDown$: Subject<KeyboardEvent>;
|
|
51
|
+
/** addEventListener 로 등록 시 일반 메서드는 this 가 컨테이너로 바뀌므로 화살표로 바인딩 */
|
|
52
|
+
private _keydownHandler;
|
|
53
|
+
private _mouseenterHandler;
|
|
54
|
+
private _mouseleaveHandler;
|
|
55
|
+
/**
|
|
56
|
+
* director 에 scene 을 추가하는 메서드
|
|
57
|
+
*/
|
|
30
58
|
addScene(scene: Scene): {
|
|
31
59
|
type: any;
|
|
32
60
|
uniqueId: string;
|
|
33
61
|
data: any;
|
|
34
62
|
director: import("./types.balcan").Director;
|
|
63
|
+
onDestroy: () => void;
|
|
35
64
|
};
|
|
65
|
+
/**
|
|
66
|
+
* stage 에서 scene 을 제거합니다. scene 에서 정의한 onDestory 를 호출해서 클리어를 유도 합니다
|
|
67
|
+
*/
|
|
36
68
|
removeScene(uniqueId: string): void;
|
|
69
|
+
/**
|
|
70
|
+
* stage 에서 모든 scene 을 제거합니다.
|
|
71
|
+
*/
|
|
37
72
|
removeAllScenes(): void;
|
|
73
|
+
/**
|
|
74
|
+
* stage 에 actor 을 추가합니다.
|
|
75
|
+
*/
|
|
38
76
|
addActor(actor: Actor): {
|
|
39
77
|
type: any;
|
|
40
78
|
entityId: string;
|
|
@@ -45,13 +83,22 @@ export declare class Director {
|
|
|
45
83
|
addPlugin?: (plugin: import("./types.balcan").Plugin) => void;
|
|
46
84
|
removePlugin?: (pluginKey: string) => void;
|
|
47
85
|
onDestroy?: () => void;
|
|
48
|
-
onBeforeRender?: () => void;
|
|
49
|
-
onRender?: () => void;
|
|
50
|
-
onUpdate?: () => void;
|
|
51
86
|
};
|
|
87
|
+
/**
|
|
88
|
+
* actor 의 id 를 변경할 수 있습니다
|
|
89
|
+
*/
|
|
52
90
|
changeActorId(oldId: string, newId: string): void;
|
|
91
|
+
/**
|
|
92
|
+
* stage 에서 actor 을 제거합니다. actor 에서 정의한 onDestory 를 호출해서 클리어를 유도 합니다
|
|
93
|
+
*/
|
|
53
94
|
removeActor(uniqueId: string): void;
|
|
95
|
+
/**
|
|
96
|
+
* stage 에서 모든 actor 을 제거합니다.
|
|
97
|
+
*/
|
|
54
98
|
removeAllActors(): void;
|
|
99
|
+
/**
|
|
100
|
+
* stage 에 staff 을 추가합니다.
|
|
101
|
+
*/
|
|
55
102
|
addStaff(staff: Staff): {
|
|
56
103
|
type: any;
|
|
57
104
|
entityId: string;
|
|
@@ -62,12 +109,66 @@ export declare class Director {
|
|
|
62
109
|
addPlugin?: (plugin: import("./types.balcan").Plugin) => void;
|
|
63
110
|
removePlugin?: (pluginKey: string) => void;
|
|
64
111
|
onDestroy?: () => void;
|
|
65
|
-
onBeforeRender?: () => void;
|
|
66
|
-
onRender?: () => void;
|
|
67
112
|
};
|
|
113
|
+
/**
|
|
114
|
+
* stage 에서 모든 staff 을 제거합니다.
|
|
115
|
+
*/
|
|
68
116
|
removeAllStaffs(): void;
|
|
117
|
+
/**
|
|
118
|
+
* stage 에서 staff 을 제거합니다. staff 에서 정의한 onDestory 를 호출해서 클리어를 유도 합니다
|
|
119
|
+
*/
|
|
69
120
|
removeStaff(uniqueId: string): void;
|
|
121
|
+
get stage(): BalcanStage;
|
|
122
|
+
/** 마우스 포인터가 stage(div) 위에 있는지 여부 */
|
|
123
|
+
get isPointerOverStage(): boolean;
|
|
124
|
+
/**
|
|
125
|
+
* stage 의 현재 scale 을 반환합니다
|
|
126
|
+
*/
|
|
70
127
|
get scale(): number;
|
|
128
|
+
/**
|
|
129
|
+
* stage 의 scale 을 변경합니다
|
|
130
|
+
*/
|
|
71
131
|
set scale(scale: number);
|
|
72
|
-
|
|
132
|
+
/** 마우스 커서 모양을 변경한다 */
|
|
133
|
+
changeMouseCursor(cursorStyle: string): void;
|
|
134
|
+
/** 현재 마우스 위치에 있는 첫 번째 shape 를 가져온다 */
|
|
135
|
+
pickShape(): import("konva/lib/Shape").Shape<import("konva/lib/Shape").ShapeConfig> | null;
|
|
136
|
+
/** 현재 마우스 위치에 있는 모든 shape 목록을 가져온다 */
|
|
137
|
+
pickShapes(): import("konva/lib/Shape").Shape<import("konva/lib/Shape").ShapeConfig>[];
|
|
138
|
+
/** 현재 마우스포인터의 pose + position 계산 */
|
|
139
|
+
getPointerPosePosition(originPoint: BalcanGeo.Vector2 | BalcanGeo.Pose, pixelPerMeter: number): BalcanGeo.PosePosition;
|
|
140
|
+
/** 여러 포지션을 변환하는 도구모음 클로져 */
|
|
141
|
+
positionConverter(): {
|
|
142
|
+
/** viewport 의 중심점은 월드좌표의 어디에 해당하는지 계산함 */
|
|
143
|
+
viewportCenterToWorld(): {
|
|
144
|
+
x: number;
|
|
145
|
+
y: number;
|
|
146
|
+
};
|
|
147
|
+
/** viewport 좌표를 월드좌표로 변환함 */
|
|
148
|
+
viewportToWorld(vector: BalcanGeo.Vector2): {
|
|
149
|
+
x: number;
|
|
150
|
+
y: number;
|
|
151
|
+
};
|
|
152
|
+
/** 월드좌표를 viewport 좌표로 변환함 */
|
|
153
|
+
worldToViewport(vector: BalcanGeo.Vector2): BalcanGeo.Vector2;
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* 어떠한 위치를 지정하여 viewport 의 중앙으로 가져다 놓는다
|
|
157
|
+
* (= 카메라 초점을 어떠한 위치로 이동한다)
|
|
158
|
+
*/
|
|
159
|
+
moveCamera(params: MoveCameraParams): void;
|
|
73
160
|
}
|
|
161
|
+
type MoveCameraParams = {
|
|
162
|
+
/** 지정한 shape 로 카메라를 이동함 */
|
|
163
|
+
type: 'shape';
|
|
164
|
+
shape: Konva.Shape | Konva.Group;
|
|
165
|
+
} | {
|
|
166
|
+
/** 지정한 viewport 좌표로 카메라를 이동함 */
|
|
167
|
+
type: 'viewportPosition';
|
|
168
|
+
vector: BalcanGeo.Vector2;
|
|
169
|
+
} | {
|
|
170
|
+
/** 지정한 월드 좌표로 카메라를 이동함 */
|
|
171
|
+
type: 'worldPosition';
|
|
172
|
+
vector: BalcanGeo.Vector2;
|
|
173
|
+
};
|
|
174
|
+
export {};
|
package/dist/core/core.balcan.js
CHANGED
|
@@ -1,61 +1,123 @@
|
|
|
1
|
+
import Konva from 'konva';
|
|
1
2
|
import { Subject } from 'rxjs';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
import { math } from './math.balcan';
|
|
4
|
+
import { util } from './util.balcan';
|
|
5
|
+
const directors = {};
|
|
6
|
+
/**
|
|
7
|
+
* 새 Director 를 생성
|
|
8
|
+
* @param divQuerySelector Konva.Stage 가 생성될 div 의 querySelector
|
|
9
|
+
*/
|
|
10
|
+
export function createDirector(divQuerySelector) {
|
|
11
|
+
if (divQuerySelector in directors) {
|
|
12
|
+
console.error('Director with the same name already exists.');
|
|
13
|
+
return directors[divQuerySelector];
|
|
14
|
+
}
|
|
15
|
+
const div = document.querySelector(divQuerySelector);
|
|
16
|
+
if (!div) {
|
|
17
|
+
console.error('Div element not found.');
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
const stage = new Konva.Stage({
|
|
21
|
+
container: div,
|
|
22
|
+
width: div.clientWidth,
|
|
23
|
+
height: div.clientHeight,
|
|
24
|
+
draggable: true,
|
|
25
|
+
});
|
|
26
|
+
const director = new Director(divQuerySelector, stage);
|
|
27
|
+
directors[divQuerySelector] = director;
|
|
28
|
+
return director;
|
|
11
29
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
directors: $store.directors,
|
|
17
|
-
};
|
|
18
|
-
function createDirector(name, stage) {
|
|
19
|
-
if (!stage) {
|
|
20
|
-
console.error('Konva Stage is required to create a new stage.');
|
|
21
|
-
return undefined;
|
|
22
|
-
}
|
|
23
|
-
const director = new Director(name, stage);
|
|
24
|
-
$store.addDirector(director);
|
|
25
|
-
return director;
|
|
30
|
+
export function getDirector(divQuerySelector) {
|
|
31
|
+
if (!(divQuerySelector in directors)) {
|
|
32
|
+
console.error(`Director with the name ${divQuerySelector} not found.`);
|
|
33
|
+
return undefined;
|
|
26
34
|
}
|
|
35
|
+
return directors[divQuerySelector];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 기존 Director 를 회수합니다
|
|
39
|
+
* @param divQuerySelector Director 가 생성된 div 의 querySelector
|
|
40
|
+
*/
|
|
41
|
+
export function destroyDirector(divQuerySelector) {
|
|
42
|
+
const director = directors[divQuerySelector];
|
|
43
|
+
director?.dispose();
|
|
44
|
+
delete directors[divQuerySelector];
|
|
27
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* 디렉터는 하나의 Konva.Stage 와 그 위에 올라올 수 있는 Balcan 요소들을 Containing 합니다.
|
|
48
|
+
* Konva.Stage 는 보통 하나의 div 에 매칭되기 때문에 Director 역시 하나의 Div와 매칭된다고 봐도 좋습니다.
|
|
49
|
+
*/
|
|
28
50
|
export class Director {
|
|
29
51
|
name;
|
|
30
|
-
|
|
52
|
+
_stage;
|
|
53
|
+
_isPointerOverStage = false;
|
|
31
54
|
scenes;
|
|
32
55
|
actors;
|
|
33
56
|
staffs;
|
|
34
57
|
constructor(name, stage) {
|
|
35
58
|
this.name = name;
|
|
36
|
-
this.
|
|
59
|
+
this._stage = stage;
|
|
37
60
|
this.staffs = {};
|
|
38
61
|
this.scenes = {};
|
|
39
62
|
this.actors = {};
|
|
40
|
-
|
|
41
|
-
stage.container().addEventListener('keydown', (e) => {
|
|
42
|
-
this.onKeyDown$.next(e);
|
|
43
|
-
});
|
|
44
|
-
stage.on('click', () => {
|
|
45
|
-
stage.container().tabIndex = 1;
|
|
46
|
-
stage.container().focus();
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
_doubleClickEvent() {
|
|
50
|
-
this.stage.on('dblclick', (e) => {
|
|
63
|
+
util.stageDoubleClickEvent(stage, (e) => {
|
|
51
64
|
this.onMouseDoubleClicked$.next(e);
|
|
52
|
-
this.stage.off('dblclick');
|
|
53
|
-
setTimeout(() => this._doubleClickEvent(), 400);
|
|
54
65
|
});
|
|
66
|
+
stage.container().addEventListener('keydown', this._keydownHandler);
|
|
67
|
+
stage.container().addEventListener('mouseenter', this._mouseenterHandler);
|
|
68
|
+
stage.container().addEventListener('mouseleave', this._mouseleaveHandler);
|
|
69
|
+
stage.on('click', this._clickHandler);
|
|
70
|
+
}
|
|
71
|
+
/** 내부의 모든 자원을 회수합니다 */
|
|
72
|
+
dispose() {
|
|
73
|
+
this.removeAllScenes();
|
|
74
|
+
this.removeAllActors();
|
|
75
|
+
this.removeAllStaffs();
|
|
76
|
+
this._stage
|
|
77
|
+
.container()
|
|
78
|
+
.removeEventListener('keydown', this._keydownHandler);
|
|
79
|
+
this._stage
|
|
80
|
+
.container()
|
|
81
|
+
.removeEventListener('mouseenter', this._mouseenterHandler);
|
|
82
|
+
this._stage
|
|
83
|
+
.container()
|
|
84
|
+
.removeEventListener('mouseleave', this._mouseleaveHandler);
|
|
85
|
+
this._stage.off('click', this._clickHandler);
|
|
86
|
+
this._stage.off('dblclick');
|
|
87
|
+
this._stage.destroyChildren();
|
|
88
|
+
this._stage.destroy();
|
|
89
|
+
delete directors[this.name];
|
|
55
90
|
}
|
|
91
|
+
_clickHandler(e) {
|
|
92
|
+
e.currentTarget.container().tabIndex = 1;
|
|
93
|
+
e.currentTarget.container().focus();
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* 이 클래스의 set scale 을 사용하여 스케일 변경시 발생하는 이벤트
|
|
97
|
+
*/
|
|
56
98
|
onScaleChanged$ = new Subject();
|
|
99
|
+
/**
|
|
100
|
+
* stage 에서 더블 클릭 이벤트
|
|
101
|
+
*/
|
|
57
102
|
onMouseDoubleClicked$ = new Subject();
|
|
103
|
+
/**
|
|
104
|
+
* stage 에서 키보드 down 이벤트
|
|
105
|
+
*/
|
|
58
106
|
onKeyDown$ = new Subject();
|
|
107
|
+
/** addEventListener 로 등록 시 일반 메서드는 this 가 컨테이너로 바뀌므로 화살표로 바인딩 */
|
|
108
|
+
_keydownHandler = (e) => {
|
|
109
|
+
if (this.onKeyDown$)
|
|
110
|
+
this.onKeyDown$.next(e);
|
|
111
|
+
};
|
|
112
|
+
_mouseenterHandler = () => {
|
|
113
|
+
this._isPointerOverStage = true;
|
|
114
|
+
};
|
|
115
|
+
_mouseleaveHandler = () => {
|
|
116
|
+
this._isPointerOverStage = false;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* director 에 scene 을 추가하는 메서드
|
|
120
|
+
*/
|
|
59
121
|
addScene(scene) {
|
|
60
122
|
if (this.scenes[scene.uniqueId]) {
|
|
61
123
|
console.error('Scene with the same uniqueId already exists.');
|
|
@@ -64,19 +126,31 @@ export class Director {
|
|
|
64
126
|
this.scenes[scene.uniqueId] = scene;
|
|
65
127
|
return scene;
|
|
66
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* stage 에서 scene 을 제거합니다. scene 에서 정의한 onDestory 를 호출해서 클리어를 유도 합니다
|
|
131
|
+
*/
|
|
67
132
|
removeScene(uniqueId) {
|
|
68
|
-
|
|
133
|
+
this.scenes[uniqueId]?.onDestroy();
|
|
69
134
|
delete this.scenes[uniqueId];
|
|
70
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* stage 에서 모든 scene 을 제거합니다.
|
|
138
|
+
*/
|
|
71
139
|
removeAllScenes() {
|
|
72
140
|
Object.keys(this.scenes).forEach((k) => {
|
|
73
141
|
this.removeScene(k);
|
|
74
142
|
});
|
|
75
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* stage 에 actor 을 추가합니다.
|
|
146
|
+
*/
|
|
76
147
|
addActor(actor) {
|
|
77
148
|
this.actors[actor.uniqueId] = actor;
|
|
78
149
|
return actor;
|
|
79
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* actor 의 id 를 변경할 수 있습니다
|
|
153
|
+
*/
|
|
80
154
|
changeActorId(oldId, newId) {
|
|
81
155
|
const actor = this.actors[oldId];
|
|
82
156
|
if (!actor)
|
|
@@ -85,52 +159,159 @@ export class Director {
|
|
|
85
159
|
this.actors[newId] = actor;
|
|
86
160
|
delete this.actors[oldId];
|
|
87
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* stage 에서 actor 을 제거합니다. actor 에서 정의한 onDestory 를 호출해서 클리어를 유도 합니다
|
|
164
|
+
*/
|
|
88
165
|
removeActor(uniqueId) {
|
|
89
166
|
this.actors[uniqueId]?.onDestroy?.();
|
|
90
167
|
delete this.actors[uniqueId];
|
|
91
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* stage 에서 모든 actor 을 제거합니다.
|
|
171
|
+
*/
|
|
92
172
|
removeAllActors() {
|
|
93
173
|
Object.keys(this.actors).forEach((k) => {
|
|
94
174
|
this.removeActor(k);
|
|
95
175
|
});
|
|
96
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* stage 에 staff 을 추가합니다.
|
|
179
|
+
*/
|
|
97
180
|
addStaff(staff) {
|
|
98
181
|
this.staffs[staff.uniqueId] = staff;
|
|
99
182
|
return staff;
|
|
100
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* stage 에서 모든 staff 을 제거합니다.
|
|
186
|
+
*/
|
|
101
187
|
removeAllStaffs() {
|
|
102
188
|
Object.keys(this.staffs).forEach((k) => {
|
|
103
189
|
this.removeStaff(k);
|
|
104
190
|
});
|
|
105
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* stage 에서 staff 을 제거합니다. staff 에서 정의한 onDestory 를 호출해서 클리어를 유도 합니다
|
|
194
|
+
*/
|
|
106
195
|
removeStaff(uniqueId) {
|
|
107
196
|
this.staffs[uniqueId]?.onDestroy?.();
|
|
108
197
|
delete this.staffs[uniqueId];
|
|
109
198
|
}
|
|
199
|
+
get stage() {
|
|
200
|
+
return this._stage;
|
|
201
|
+
}
|
|
202
|
+
/** 마우스 포인터가 stage(div) 위에 있는지 여부 */
|
|
203
|
+
get isPointerOverStage() {
|
|
204
|
+
return this._isPointerOverStage;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* stage 의 현재 scale 을 반환합니다
|
|
208
|
+
*/
|
|
110
209
|
get scale() {
|
|
111
|
-
return this.
|
|
210
|
+
return this._stage.scale().x;
|
|
112
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* stage 의 scale 을 변경합니다
|
|
214
|
+
*/
|
|
113
215
|
set scale(scale) {
|
|
114
|
-
this.
|
|
216
|
+
this._stage.scale({
|
|
115
217
|
x: scale,
|
|
116
218
|
y: scale,
|
|
117
219
|
});
|
|
118
220
|
this.onScaleChanged$.next({ scale });
|
|
119
221
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
222
|
+
/** 마우스 커서 모양을 변경한다 */
|
|
223
|
+
changeMouseCursor(cursorStyle) {
|
|
224
|
+
this.stage.container().style.cursor = cursorStyle;
|
|
225
|
+
}
|
|
226
|
+
/** 현재 마우스 위치에 있는 첫 번째 shape 를 가져온다 */
|
|
227
|
+
pickShape() {
|
|
228
|
+
return this.stage.getIntersection(this.stage.getPointerPosition() ?? {
|
|
229
|
+
x: 0,
|
|
230
|
+
y: 0,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
/** 현재 마우스 위치에 있는 모든 shape 목록을 가져온다 */
|
|
234
|
+
pickShapes() {
|
|
235
|
+
return this.stage.getAllIntersections(this.stage.getPointerPosition() ?? {
|
|
236
|
+
x: 0,
|
|
237
|
+
y: 0,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
/** 현재 마우스포인터의 pose + position 계산 */
|
|
241
|
+
getPointerPosePosition(originPoint, pixelPerMeter) {
|
|
242
|
+
if (!this.isPointerOverStage) {
|
|
243
|
+
return {
|
|
244
|
+
pose: {
|
|
245
|
+
x: 0,
|
|
246
|
+
y: 0,
|
|
247
|
+
theta: 0,
|
|
248
|
+
},
|
|
249
|
+
position: {
|
|
250
|
+
stagex: 0,
|
|
251
|
+
stagey: 0,
|
|
252
|
+
degree: 0,
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
const pointer = this.stage.getRelativePointerPosition();
|
|
257
|
+
const position = {
|
|
258
|
+
stagex: pointer?.x ?? 0,
|
|
259
|
+
stagey: pointer?.y ?? 0,
|
|
260
|
+
degree: 0,
|
|
261
|
+
};
|
|
262
|
+
const pose = math.transformPosition(position, originPoint, pixelPerMeter);
|
|
263
|
+
return {
|
|
264
|
+
pose,
|
|
265
|
+
position,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/** 여러 포지션을 변환하는 도구모음 클로져 */
|
|
269
|
+
positionConverter() {
|
|
270
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
271
|
+
const director = this;
|
|
272
|
+
return {
|
|
273
|
+
/** viewport 의 중심점은 월드좌표의 어디에 해당하는지 계산함 */
|
|
274
|
+
viewportCenterToWorld() {
|
|
275
|
+
return this.viewportToWorld({
|
|
276
|
+
x: director.stage.width() / 2,
|
|
277
|
+
y: director.stage.height() / 2,
|
|
278
|
+
});
|
|
279
|
+
},
|
|
280
|
+
/** viewport 좌표를 월드좌표로 변환함 */
|
|
281
|
+
viewportToWorld(vector) {
|
|
282
|
+
const offset = math.vectorSubtract(vector, director.stage.getAbsolutePosition());
|
|
283
|
+
return math.vectorDivide(offset, director.scale);
|
|
284
|
+
},
|
|
285
|
+
/** 월드좌표를 viewport 좌표로 변환함 */
|
|
286
|
+
worldToViewport(vector) {
|
|
287
|
+
const scaledOffset = math.vectorMultiply(vector, director.scale);
|
|
288
|
+
return math.vectorAdd(director.stage.getAbsolutePosition(), scaledOffset);
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* 어떠한 위치를 지정하여 viewport 의 중앙으로 가져다 놓는다
|
|
294
|
+
* (= 카메라 초점을 어떠한 위치로 이동한다)
|
|
295
|
+
*/
|
|
296
|
+
moveCamera(params) {
|
|
297
|
+
if (params.type === 'viewportPosition') {
|
|
298
|
+
const moveVector = math.vectorSubtract({
|
|
299
|
+
x: this.stage.width() / 2,
|
|
300
|
+
y: this.stage.height() / 2,
|
|
301
|
+
}, params.vector);
|
|
302
|
+
this.stage.absolutePosition(math.vectorAdd(this.stage.getAbsolutePosition(), moveVector));
|
|
303
|
+
}
|
|
304
|
+
if (params.type === 'worldPosition') {
|
|
305
|
+
const centerWorldPosition = this.positionConverter().viewportCenterToWorld();
|
|
306
|
+
const moveVector = math.vectorSubtract(centerWorldPosition, params.vector);
|
|
307
|
+
const scaledMoveVector = math.vectorMultiply(moveVector, this.scale);
|
|
308
|
+
this.stage.absolutePosition(math.vectorAdd(this.stage.getAbsolutePosition(), scaledMoveVector));
|
|
127
309
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
actor.plugins.forEach((x) => x.onRender?.());
|
|
310
|
+
else if (params.type === 'shape') {
|
|
311
|
+
this.moveCamera({
|
|
312
|
+
type: 'viewportPosition',
|
|
313
|
+
vector: params.shape.getAbsolutePosition(),
|
|
314
|
+
});
|
|
134
315
|
}
|
|
135
316
|
}
|
|
136
317
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
/** 일반 수학과 기하학 관련 계산 함수들을 모아둔 네임스페이스 */
|
|
1
2
|
export declare namespace math {
|
|
3
|
+
/** 주어진 숫자들의 평균을 구한다 */
|
|
2
4
|
function average(...values: number[]): number;
|
|
3
5
|
function radian(deg: number, opt?: {
|
|
4
6
|
invertY?: boolean;
|
|
@@ -28,8 +30,73 @@ export declare namespace math {
|
|
|
28
30
|
}): number;
|
|
29
31
|
/** 벡터 덧셈 */
|
|
30
32
|
function vectorAdd<T extends BalcanGeo.Vector2 | BalcanGeo.Pose>(v1: T, v2: T): T;
|
|
33
|
+
/** 벡터 뺄셈 */
|
|
34
|
+
function vectorSubtract<T extends BalcanGeo.Vector2 | BalcanGeo.Pose>(v1: T, v2: T): T;
|
|
35
|
+
/** 벡터 곱셈 */
|
|
36
|
+
function vectorMultiply<T extends BalcanGeo.Vector2 | BalcanGeo.Pose>(v1: T, scalar: number): T;
|
|
37
|
+
/** 벡터 나눗셈 */
|
|
38
|
+
function vectorDivide<T extends BalcanGeo.Vector2 | BalcanGeo.Pose>(v1: T, scalar: number): T;
|
|
31
39
|
/** 벡터 내적 */
|
|
32
40
|
function vectorDot<T extends BalcanGeo.Vector2 | BalcanGeo.Pose>(v1: T, v2: T): number;
|
|
33
|
-
/**
|
|
41
|
+
/** 좌표형 자료형 (Position, Pose) 를 vector2d 로 변환함 */
|
|
34
42
|
function vector2d<T extends BalcanGeo.Vector2 | BalcanGeo.Position | BalcanGeo.Pose>(point: T): BalcanGeo.Vector2;
|
|
43
|
+
/** 벡터들 에 회전 행렬 적용 */
|
|
44
|
+
function rotateVectors(params: {
|
|
45
|
+
/** 회전할 점들 */
|
|
46
|
+
points: BalcanGeo.Vector2[];
|
|
47
|
+
/** 회전 각도 */
|
|
48
|
+
rotateAngle: number;
|
|
49
|
+
/** 회전 각도 단위 */
|
|
50
|
+
angleUnit?: 'degree' | 'radian';
|
|
51
|
+
/** 중심점 */
|
|
52
|
+
centerPosition?: BalcanGeo.Vector2;
|
|
53
|
+
/** Y방향 반전? true 를 할경우 회전방향이 시계방향이 되는 효과가 있음. */
|
|
54
|
+
invertY?: boolean;
|
|
55
|
+
}): {
|
|
56
|
+
x: number;
|
|
57
|
+
y: number;
|
|
58
|
+
}[];
|
|
59
|
+
/**
|
|
60
|
+
* 어떤 점 P와 어떤 선분 S가 있다고 할 때
|
|
61
|
+
* 선분 S 위에 존재할 수 있는 무수한 점들 중 - P에서 가장 가까운 점 X를 찾는다
|
|
62
|
+
* @param point 점 위치벡터 P
|
|
63
|
+
* @param segmentPosition 선분 S의 정의 (양 끝점의 좌표)
|
|
64
|
+
* @returns X점의 좌표 벡터
|
|
65
|
+
*/
|
|
66
|
+
function nearestPointOnSegment(point: BalcanGeo.Vector2, segmentPosition: [BalcanGeo.Vector2, BalcanGeo.Vector2] | [BalcanGeo.Pose, BalcanGeo.Pose]): BalcanGeo.Vector2;
|
|
67
|
+
/**
|
|
68
|
+
* 주어진 점이 주어진 다각형 내부에 있는지 확인
|
|
69
|
+
* @param point 점의 좌표
|
|
70
|
+
* @param polygon 다각형의 정의 (점들의 좌표)
|
|
71
|
+
* @returns 점이 다각형 내부에 있는지 여부
|
|
72
|
+
*/
|
|
73
|
+
function isPointInPolygon(point: BalcanGeo.Vector2, polygon: BalcanGeo.Vector2[]): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* originPoint 와 endPoint 를 잇는 선분을 가정하고
|
|
76
|
+
* extendLength 만큼 양쪽으로 늘이거나 줄인 선분을 반환
|
|
77
|
+
* (extendLength는 음수일 수도 있음)
|
|
78
|
+
*/
|
|
79
|
+
function extendSegment(params: {
|
|
80
|
+
originPoint: BalcanGeo.Vector2;
|
|
81
|
+
endPoint: BalcanGeo.Vector2;
|
|
82
|
+
extendLengthFromOrigin: number;
|
|
83
|
+
extendLengthFromEnd: number;
|
|
84
|
+
}): {
|
|
85
|
+
origin: BalcanGeo.Vector2;
|
|
86
|
+
end: BalcanGeo.Vector2;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* 실좌표계 상 위치 (Pose) 를 화면좌표계로 변환
|
|
90
|
+
* @param pose 실좌표계 상의 위치
|
|
91
|
+
* @param origin 원점을 삼으려는 화면좌표계 상 좌표
|
|
92
|
+
* @param pixelPerMeter 1미터당 픽셀 수
|
|
93
|
+
*/
|
|
94
|
+
function transformPose(pose: BalcanGeo.Pose, origin: BalcanGeo.Vector2, pixelPerMeter: number): BalcanGeo.Position;
|
|
95
|
+
/**
|
|
96
|
+
* 화면좌표계상 위치를 실좌표계로 변환
|
|
97
|
+
* @param position
|
|
98
|
+
* @param realOrigin 원점을 삼으려는 화면좌표계 상 좌표
|
|
99
|
+
* @param pixelPerMeter 1미터당 픽셀 수
|
|
100
|
+
*/
|
|
101
|
+
function transformPosition(position: BalcanGeo.Position, realOrigin: BalcanGeo.Vector2, pixelPerMeter: number): BalcanGeo.Pose;
|
|
35
102
|
}
|