@operato/scene-indoor-map 0.0.16

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 (95) hide show
  1. package/@types/global/index.d.ts +1 -0
  2. package/CHANGELOG.md +16 -0
  3. package/LICENSE +21 -0
  4. package/README.md +15 -0
  5. package/assets/beacon.png +0 -0
  6. package/assets/indoor-map.png +0 -0
  7. package/assets/no-image.png +0 -0
  8. package/assets/rack.png +0 -0
  9. package/demo/imu-mqtt-node/imu-publisher.js +66 -0
  10. package/demo/imu-mqtt-node/package.json +16 -0
  11. package/demo/index-camera.html +108 -0
  12. package/demo/index-gaussian.html +184 -0
  13. package/demo/index-indoor-map-property.html +96 -0
  14. package/demo/index-indoor-map.html +289 -0
  15. package/demo/index-rack-property.html +76 -0
  16. package/demo/index.html +365 -0
  17. package/demo/things-scene-indoor-map.html +6 -0
  18. package/dist/beacon.d.ts +19 -0
  19. package/dist/beacon.js +61 -0
  20. package/dist/beacon.js.map +1 -0
  21. package/dist/camera.d.ts +20 -0
  22. package/dist/camera.js +158 -0
  23. package/dist/camera.js.map +1 -0
  24. package/dist/editors/index.d.ts +5 -0
  25. package/dist/editors/index.js +11 -0
  26. package/dist/editors/index.js.map +1 -0
  27. package/dist/editors/things-editor-action.d.ts +7 -0
  28. package/dist/editors/things-editor-action.js +40 -0
  29. package/dist/editors/things-editor-action.js.map +1 -0
  30. package/dist/floor.d.ts +23 -0
  31. package/dist/floor.js +66 -0
  32. package/dist/floor.js.map +1 -0
  33. package/dist/index.d.ts +3 -0
  34. package/dist/index.js +10 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/indoor-map.d.ts +34 -0
  37. package/dist/indoor-map.js +161 -0
  38. package/dist/indoor-map.js.map +1 -0
  39. package/dist/quaternion.d.ts +39 -0
  40. package/dist/quaternion.js +79 -0
  41. package/dist/quaternion.js.map +1 -0
  42. package/dist/rack.d.ts +27 -0
  43. package/dist/rack.js +84 -0
  44. package/dist/rack.js.map +1 -0
  45. package/dist/templates/beacon.d.ts +15 -0
  46. package/dist/templates/beacon.js +16 -0
  47. package/dist/templates/beacon.js.map +1 -0
  48. package/dist/templates/camera.d.ts +20 -0
  49. package/dist/templates/camera.js +21 -0
  50. package/dist/templates/camera.js.map +1 -0
  51. package/dist/templates/index.d.ts +36 -0
  52. package/dist/templates/index.js +4 -0
  53. package/dist/templates/index.js.map +1 -0
  54. package/dist/templates/indoor-map.d.ts +16 -0
  55. package/dist/templates/indoor-map.js +17 -0
  56. package/dist/templates/indoor-map.js.map +1 -0
  57. package/dist/templates/rack.d.ts +22 -0
  58. package/dist/templates/rack.js +23 -0
  59. package/dist/templates/rack.js.map +1 -0
  60. package/helps/scene/component/indoor-map.ko.md +65 -0
  61. package/helps/scene/component/indoor-map.md +65 -0
  62. package/helps/scene/component/indoor-map.zh.md +65 -0
  63. package/helps/scene/component/rack.ko.md +17 -0
  64. package/helps/scene/component/rack.md +15 -0
  65. package/helps/scene/component/rack.zh.md +16 -0
  66. package/helps/scene/images/button-evnet-mapping-01.png +0 -0
  67. package/helps/scene/images/button-evnet-mapping-02.png +0 -0
  68. package/helps/scene/images/button-evnet-mapping-03.png +0 -0
  69. package/helps/scene/images/container-03.png +0 -0
  70. package/helps/scene/images/indoor-button-finish-01.gif +0 -0
  71. package/helps/scene/images/indoor-create-01.png +0 -0
  72. package/helps/scene/images/indoor-create-02.png +0 -0
  73. package/helps/scene/images/indoor-create-03.png +0 -0
  74. package/helps/scene/images/indoor-setting-01.png +0 -0
  75. package/images/icon-button.png +0 -0
  76. package/package.json +61 -0
  77. package/src/beacon.ts +69 -0
  78. package/src/camera.ts +207 -0
  79. package/src/editors/index.ts +11 -0
  80. package/src/editors/things-editor-action.ts +48 -0
  81. package/src/floor.ts +386 -0
  82. package/src/index.ts +9 -0
  83. package/src/indoor-map.ts +211 -0
  84. package/src/quaternion.ts +129 -0
  85. package/src/rack.ts +104 -0
  86. package/src/templates/beacon.ts +16 -0
  87. package/src/templates/camera.ts +21 -0
  88. package/src/templates/index.ts +4 -0
  89. package/src/templates/indoor-map.ts +18 -0
  90. package/src/templates/rack.ts +23 -0
  91. package/test/basic-test.html +67 -0
  92. package/test/index.html +22 -0
  93. package/things-scene.config.js +7 -0
  94. package/tsconfig.json +23 -0
  95. package/tsconfig.tsbuildinfo +1 -0
package/src/floor.ts ADDED
@@ -0,0 +1,386 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+ import { Component, Container } from '@hatiolab/things-scene';
5
+
6
+ import IndoorMap from './indoor-map';
7
+
8
+ const NATURE = {
9
+ mutable: false,
10
+ resizable: true,
11
+ rotatable: true,
12
+ properties: [
13
+ {
14
+ type: 'action',
15
+ label: 'remove',
16
+ name: 'remove',
17
+ property: {
18
+ icon: 'remove-circle',
19
+ action: function(floor: Floor) {
20
+ let indoor = floor.parent as IndoorMap
21
+ indoor.removeComponent(floor)
22
+ indoor.activeIndex = 0
23
+ indoor.invalidate()
24
+ }
25
+ }
26
+ }
27
+ ]
28
+ }
29
+
30
+ export default class Floor extends Container {
31
+ private _clickPoint?: Component
32
+
33
+ get hasTextProperty() {
34
+ return false
35
+ }
36
+
37
+ get showMoveHandle() {
38
+ return false
39
+ }
40
+
41
+ /*
42
+ * PATH 리스트를 직접 수정할 수 있는 지를 결정한다.
43
+ *
44
+ * 일반적으로 PATH는 바운드 생성을 위해서 논리적으로 생성되므로, 직접 수정하지 않는다.(return false)
45
+ * 그러나, 각 꼭지점들이 개별로 움직이는 다각형류는 path 라는 모델데이타를 가지므로, 직접수정이 가능할 수 있다.(return true)
46
+ *
47
+ * Immutable 컴포넌트의 형상을 바꾸는 방법은 바운드를 이용한 리사이즈나, 특별한 컨트롤을 통해서 가능하다.
48
+ */
49
+ get mutable() {
50
+ return false
51
+ }
52
+
53
+ /*
54
+ * BOUND를 통해서 리사이즈를 할 수 있는 지를 결정한다.
55
+ *
56
+ * 일반적으로 면적을 갖는 컴포넌트는 대체로 가능하다.(return true)
57
+ * 그러나, LINE 등 면적을 가지지않는 컴포넌트는 가능하지 않도록 정의한다.(return false)
58
+ */
59
+ get resizable() {
60
+ return false
61
+ }
62
+
63
+ /*
64
+ * 회전을 할 수 있는 지를 결정한다.
65
+ *
66
+ * 일반적으로 모든 컴포넌트는 가능하다.(return true)
67
+ */
68
+ get rotatable() {
69
+ return false
70
+ }
71
+
72
+ get nature() {
73
+ return NATURE
74
+ }
75
+
76
+ // drawLocationMarkers(locations) {
77
+ // for (let uuid in locations) {
78
+ // let locInfo = locations[uuid]
79
+ // let props = locInfo.props || {}
80
+
81
+ // props.width = props.width || 10
82
+ // props.height = props.height || 10
83
+
84
+ // let currentTime = new Date().getTime()
85
+ // // let diffTime = 500
86
+ // let diffTime = currentTime - locInfo.lastUpdateTime
87
+
88
+ // if (diffTime < locInfo.updateInterval) {
89
+ // let movingObject = this.findById(uuid)
90
+ // if (movingObject) {
91
+ // // props.yaw = 0;
92
+ // // props.roll = 0;
93
+
94
+ // movingObject.set(props)
95
+ // for (let key in props) {
96
+ // movingObject[key] = props[key]
97
+ // }
98
+ // } else {
99
+ // // TODO: marker의 초기값 관련 로직 정리 필요.
100
+
101
+ // let config = Object.assign(
102
+ // {
103
+ // type: locInfo.type || 'rect',
104
+ // // type: locInfo.type || "camera",
105
+ // id: uuid,
106
+ // fillStyle: 'red',
107
+ // left: props.center.x - props.width * 0.5,
108
+ // top: props.center.y - props.height * 0.5,
109
+ // cx: props.center.x,
110
+ // cy: props.center.y
111
+ // },
112
+ // props
113
+ // )
114
+
115
+ // let marker = Model.compile(config)
116
+
117
+ // this.addComponent(marker)
118
+
119
+ // // movingObject = this.findById(uuid)
120
+ // // if(movingObject) {
121
+ // // movingObject.set(props);
122
+ // // }
123
+ // }
124
+ // } else {
125
+ // let movingObject = this.findById(uuid)
126
+ // this.removeComponent(movingObject)
127
+ // }
128
+
129
+ // this.invalidate()
130
+ // }
131
+ // }
132
+
133
+ // simulate(point) {
134
+ // // for(let i in this.components) {
135
+ // // if(this.components[i].model.type != 'beacon')
136
+ // // continue;
137
+ // //
138
+ // // let beacon = this.components[i]
139
+ // // let distance = Math.sqrt(Math.pow(beacon.center.x - point.x, 2) + Math.pow(beacon.center.y - point.y, 2)) * 0.01
140
+ // // let rssi = -10 * Math.log10(distance) + (beacon.txPower || -71)
141
+ // //
142
+ // // let randRssi = gaussian(rssi, Math.pow(4.894686948810031, 2))
143
+ // //
144
+ // // rssi = randRssi.ppf(Math.random())
145
+ // //
146
+ // // console.log(rssi);
147
+ // // }
148
+
149
+ // let beacons: {
150
+ // distance: number;
151
+ // gaussian: number;
152
+ // txPower: number;
153
+ // }[] = []
154
+
155
+ // for (let i in this.components) {
156
+ // if (this.components[i].model.type != 'beacon') continue
157
+
158
+ // let beacon = this.components[i]
159
+
160
+ // beacon.distance = Math.sqrt(
161
+ // Math.pow(beacon.center.x - point.x, 2) +
162
+ // Math.pow(beacon.center.y - point.y, 2)
163
+ // )
164
+ // beacon.gaussian = gaussian(
165
+ // beacon.model.txPower || -71,
166
+ // Math.pow(3.209, 2)
167
+ // )
168
+ // beacon.txPower = beacon.gaussian.ppf(Math.random())
169
+
170
+ // beacons.push(beacon)
171
+ // }
172
+
173
+ // beacons = beacons.slice(0)
174
+
175
+ // this.calculatePosition(beacons, point)
176
+ // }
177
+
178
+ // calculatePosition(nodeArr, position) {
179
+ // let beacons = nodeArr
180
+
181
+ // beacons.sort(function(a, b) {
182
+ // let rssiA = -10 * Math.log10(a.distance) + a.txPower
183
+ // let rssiB = -10 * Math.log10(b.distance) + b.txPower
184
+
185
+ // return Math.abs(rssiA) - Math.abs(rssiB)
186
+ // })
187
+
188
+ // let beaconCombs = this.k_combinations(beacons.slice(0, 4), 3)
189
+ // let positions = []
190
+
191
+ // for (let i in beaconCombs) {
192
+ // let beaconComb = beaconCombs[i]
193
+ // let beaconA = beaconComb[0]
194
+ // let beaconB = beaconComb[1]
195
+ // let beaconC = beaconComb[2]
196
+
197
+ // let xa = beaconA.center.x
198
+ // let ya = beaconA.center.y
199
+ // let xb = beaconB.center.x
200
+ // let yb = beaconB.center.y
201
+ // let xc = beaconC.center.x
202
+ // let yc = beaconC.center.y
203
+ // let ra = beaconA.distance
204
+ // let rb = beaconB.distance
205
+ // let rc = beaconC.distance
206
+
207
+ // // let ra = Math.sqrt(Math.pow(beaconA.center.x - position.x, 2) + Math.pow(beaconA.center.y - position.y, 2)) * 0.01
208
+ // // let rb = Math.sqrt(Math.pow(beaconB.center.x - position.x, 2) + Math.pow(beaconB.center.y - position.y, 2)) * 0.01
209
+ // // let rc = Math.sqrt(Math.pow(beaconC.center.x - position.x, 2) + Math.pow(beaconC.center.y - position.y, 2)) * 0.01
210
+
211
+ // let rssiA = -10 * Math.log10(beaconA.distance * 0.01) + beaconA.txPower
212
+ // let rssiB = -10 * Math.log10(beaconB.distance * 0.01) + beaconB.txPower
213
+ // let rssiC = -10 * Math.log10(beaconC.distance * 0.01) + beaconC.txPower
214
+
215
+ // ra = this.calculateDistance(beaconA.txPower, rssiA) * 100
216
+ // rb = this.calculateDistance(beaconB.txPower, rssiB) * 100
217
+ // rc = this.calculateDistance(beaconC.txPower, rssiC) * 100
218
+
219
+ // let xaSq = xa * xa,
220
+ // xbSq = xb * xb,
221
+ // xcSq = xc * xc,
222
+ // yaSq = ya * ya,
223
+ // ybSq = yb * yb,
224
+ // ycSq = yc * yc,
225
+ // raSq = ra * ra,
226
+ // rbSq = rb * rb,
227
+ // rcSq = rc * rc
228
+ // let numerator1 =
229
+ // (xb - xa) * (xcSq + ycSq - rcSq) +
230
+ // (xa - xc) * (xbSq + ybSq - rbSq) +
231
+ // (xc - xb) * (xaSq + yaSq - raSq)
232
+ // let denominator1 = 2 * (yc * (xb - xa) + yb * (xa - xc) + ya * (xc - xb))
233
+ // let y = numerator1 / denominator1
234
+ // let numerator2 =
235
+ // rbSq - raSq + xaSq - xbSq + yaSq - ybSq - 2 * (ya - yb) * y
236
+ // let denominator2 = 2 * (xa - xb)
237
+ // let x = numerator2 / denominator2
238
+
239
+ // if (Number.isFinite(x) && Number.isFinite(y)) {
240
+ // positions.push({
241
+ // x: x,
242
+ // y: y
243
+ // })
244
+ // }
245
+ // }
246
+
247
+ // let avgPosition = this.averageOfPositions(positions)
248
+
249
+ // if (this._simPosition) this.removeComponent(this._simPosition)
250
+
251
+ // this._simPosition = Model.compile({
252
+ // type: 'ellipse',
253
+ // cx: avgPosition.x,
254
+ // cy: avgPosition.y,
255
+ // rx: 10,
256
+ // ry: 10,
257
+ // fillStyle: 'navy'
258
+ // })
259
+
260
+ // this.addComponent(this._simPosition)
261
+ // }
262
+
263
+ // calculateDistance(txPower, rssi) {
264
+ // if (rssi == 0) {
265
+ // return -1.0 // if we cannot determine distance, return -1.
266
+ // }
267
+
268
+ // let ratio = (rssi * 1.0) / txPower
269
+ // if (ratio < 1.0) {
270
+ // return Math.pow(ratio, 10)
271
+ // } else {
272
+ // let accuracy = 0.89976 * Math.pow(ratio, 7.7095) + 0.111
273
+ // return accuracy
274
+ // }
275
+ // }
276
+
277
+ // calculateAngle(p1, p2, p3) {
278
+ // let l1 = Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2),
279
+ // l2 = Math.pow(p2.x - p3.x, 2) + Math.pow(p2.y - p3.y, 2),
280
+ // l3 = Math.pow(p3.x - p1.x, 2) + Math.pow(p3.y - p1.y, 2)
281
+
282
+ // return Math.acos((l1 + l2 - l3) / Math.sqrt(4 * l1 * l2))
283
+ // }
284
+
285
+ // averageOfPositions(p) {
286
+ // let sumOfX = 0
287
+ // let sumOfY = 0
288
+
289
+ // for (let i in p) {
290
+ // let point = p[i]
291
+ // sumOfX += point.x
292
+ // sumOfY += point.y
293
+ // }
294
+
295
+ // return {
296
+ // x: sumOfX / p.length,
297
+ // y: sumOfY / p.length
298
+ // }
299
+ // }
300
+
301
+ // k_combinations(set, k) {
302
+ // var i, j, combs, head, tailcombs
303
+
304
+ // // There is no way to take e.g. sets of 5 elements from
305
+ // // a set of 4.
306
+ // if (k > set.length || k <= 0) {
307
+ // return []
308
+ // }
309
+
310
+ // // K-sized set has only one K-sized subset.
311
+ // if (k == set.length) {
312
+ // return [set]
313
+ // }
314
+
315
+ // // There is N 1-sized subsets in a N-sized set.
316
+ // if (k == 1) {
317
+ // combs = []
318
+ // for (i = 0; i < set.length; i++) {
319
+ // combs.push([set[i]])
320
+ // }
321
+ // return combs
322
+ // }
323
+
324
+ // // Assert {1 < k < set.length}
325
+
326
+ // // Algorithm description:
327
+ // // To get k-combinations of a set, we want to join each element
328
+ // // with all (k-1)-combinations of the other elements. The set of
329
+ // // these k-sized sets would be the desired result. However, as we
330
+ // // represent sets with lists, we need to take duplicates into
331
+ // // account. To avoid producing duplicates and also unnecessary
332
+ // // computing, we use the following approach: each element i
333
+ // // divides the list into three: the preceding elements, the
334
+ // // current element i, and the subsequent elements. For the first
335
+ // // element, the list of preceding elements is empty. For element i,
336
+ // // we compute the (k-1)-computations of the subsequent elements,
337
+ // // join each with the element i, and store the joined to the set of
338
+ // // computed k-combinations. We do not need to take the preceding
339
+ // // elements into account, because they have already been the i:th
340
+ // // element so they are already computed and stored. When the length
341
+ // // of the subsequent list drops below (k-1), we cannot find any
342
+ // // (k-1)-combs, hence the upper limit for the iteration:
343
+ // combs = []
344
+ // for (i = 0; i < set.length - k + 1; i++) {
345
+ // // head is a list that includes only our current element.
346
+ // head = set.slice(i, i + 1)
347
+ // // We take smaller combinations from the subsequent elements
348
+ // tailcombs = this.k_combinations(set.slice(i + 1), k - 1)
349
+ // // For each (k-1)-combination we join it with the current
350
+ // // and store it to the set of k-combinations.
351
+ // for (j = 0; j < tailcombs.length; j++) {
352
+ // combs.push(head.concat(tailcombs[j]))
353
+ // }
354
+ // }
355
+ // return combs
356
+ // }
357
+
358
+ // onclick(e) {
359
+ // return
360
+
361
+ // let point = this.transcoordC2S(e.offsetX, e.offsetY)
362
+
363
+ // if (this._clickPoint) {
364
+ // this.removeComponent(this._clickPoint)
365
+ // }
366
+
367
+ // this._clickPoint = Model.compile({
368
+ // type: 'ellipse',
369
+ // cx: point.x,
370
+ // cy: point.y,
371
+ // rx: 10,
372
+ // ry: 10,
373
+ // fillStyle: 'red'
374
+ // })
375
+
376
+ // this.addComponent(this._clickPoint)
377
+ // this.simulate(point)
378
+ // // let self = this
379
+ // // setTimeout(function() {
380
+ // // self.simulate(point)
381
+ // // }, 500)
382
+ // this.invalidate()
383
+ // }
384
+ }
385
+
386
+ Component.register('floor', Floor)
package/src/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+ export { default as Floor } from './floor'
5
+ export { default as IndoorMap } from './indoor-map'
6
+ export { default as Rack } from './rack'
7
+ // export { default as Beacon } from './beacon'
8
+ // export { default as Camera } from './camera'
9
+ // export { default as Quaternion } from './quaternion'
@@ -0,0 +1,211 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+ import {
5
+ CardLayout,
6
+ Component,
7
+ Container,
8
+ Model,
9
+ POINT,
10
+ } from '@hatiolab/things-scene';
11
+
12
+ import Floor from './floor';
13
+
14
+ const LABEL_WIDTH = 25
15
+ const LABEL_HEIGHT = 25
16
+
17
+ function rgba(r: number, g: number, b: number, a: number) {
18
+ return `rgba(${r}, ${g}, ${b}, ${a})`
19
+ }
20
+
21
+ const NATURE = {
22
+ mutable: false,
23
+ resizable: true,
24
+ rotatable: true,
25
+ properties: [
26
+ {
27
+ type: 'action',
28
+ label: 'floor',
29
+ name: 'floor',
30
+ property: {
31
+ icon: 'add-circle',
32
+ action: (indoorMap: IndoorMap) => {
33
+ indoorMap.addFloor()
34
+ }
35
+ }
36
+ }
37
+ ],
38
+ 'value-property': 'activeIndex',
39
+ help: 'scene/component/indoor-map'
40
+ }
41
+
42
+ export default class IndoorMap extends Container {
43
+ private _focused: boolean = false
44
+ private __down_point?: POINT
45
+
46
+ get nature() {
47
+ return NATURE
48
+ }
49
+
50
+ get layout() {
51
+ return CardLayout
52
+ }
53
+
54
+ get activeIndex() {
55
+ var config = Object.assign({}, this.layoutConfig)
56
+ return config.activeIndex
57
+ }
58
+
59
+ set activeIndex(index) {
60
+ var config = Object.assign({}, this.layoutConfig)
61
+ config.activeIndex = index
62
+ this.layoutConfig = config
63
+ }
64
+
65
+ get layoutConfig() {
66
+ return this.get('layoutConfig')
67
+ }
68
+
69
+ set layoutConfig(config) {
70
+ this.set('layoutConfig', config)
71
+ }
72
+
73
+ get activeFloor(): Floor {
74
+ return this.components[this.get('layoutConfig').activeIndex] as Floor
75
+ }
76
+
77
+ ready() {
78
+ super.ready()
79
+
80
+ if (this.components.length == 0) this.addFloor()
81
+ }
82
+
83
+ postrender(context: CanvasRenderingContext2D) {
84
+ super.postrender(context)
85
+
86
+ if (this.app.isViewMode) return
87
+
88
+ if (!this._focused) return
89
+
90
+ var { left, top, width, fillStyle } = this.model
91
+
92
+ // floor 선택 탭 그리기
93
+ for (let i = 0; i < this.components.length; i++) {
94
+ context.beginPath()
95
+
96
+ context.rect(
97
+ left - LABEL_WIDTH,
98
+ top + i * LABEL_HEIGHT,
99
+ LABEL_WIDTH,
100
+ LABEL_HEIGHT
101
+ )
102
+
103
+ let color = 255 - ((20 * (i + 1)) % 255)
104
+ context.fillStyle = rgba(color, color, color, 1)
105
+ context.fill()
106
+
107
+ context.closePath()
108
+ }
109
+
110
+ context.beginPath()
111
+
112
+ context.moveTo(left, top)
113
+ context.lineTo(left - LABEL_WIDTH, top)
114
+ context.lineTo(
115
+ left - LABEL_WIDTH,
116
+ top + this.components.length * LABEL_HEIGHT
117
+ )
118
+ context.lineTo(left, top + this.components.length * LABEL_HEIGHT)
119
+
120
+ context.strokeStyle = '#ccc'
121
+ context.stroke()
122
+
123
+ context.closePath()
124
+ }
125
+
126
+ contains(x: number, y: number) {
127
+ var contains = super.contains(x, y)
128
+
129
+ if (this.app.isViewMode) return contains
130
+
131
+ var { left, top, width } = this.bounds
132
+ var h = LABEL_HEIGHT
133
+
134
+ contains =
135
+ contains ||
136
+ // card selector 영역에 포함되는지
137
+ (x < Math.max(left - LABEL_WIDTH, left) &&
138
+ x > Math.min(left - LABEL_WIDTH, left) &&
139
+ y < Math.max(top + h * this.size(), top) &&
140
+ y > Math.min(top + h * this.size(), top))
141
+
142
+ if (contains) this._focused = true
143
+ else this._focused = false
144
+
145
+ this.invalidate()
146
+ return contains
147
+ }
148
+
149
+ onmouseup(e: MouseEvent) {
150
+ var down_point = this.__down_point
151
+ delete this.__down_point
152
+
153
+ if (!down_point || down_point.x != e.offsetX || down_point.y != e.offsetY) {
154
+ return
155
+ }
156
+
157
+ var point = this.transcoordC2S(e.offsetX, e.offsetY)
158
+
159
+ var { left, top } = this.model
160
+
161
+ var x = point.x - left
162
+ var y = point.y - top
163
+
164
+ if (x > 0) return
165
+
166
+ y /= LABEL_HEIGHT
167
+ y = Math.floor(y)
168
+
169
+ if (!this.layoutConfig) this.layoutConfig = {}
170
+
171
+ if (y >= this.components.length) return
172
+
173
+ // /* 생성 버튼이 클릭되면, 새로운 floor를 추가한다. */
174
+ // if(y == this.components.length) {
175
+ // this.add(Model.compile({
176
+ // type: 'floor',
177
+ // width: 100,
178
+ // height: 100
179
+ // }))
180
+ // }
181
+ this.activeIndex = y
182
+ }
183
+
184
+ onmousedown(e: MouseEvent) {
185
+ this.__down_point = {
186
+ x: e.offsetX,
187
+ y: e.offsetY
188
+ }
189
+ }
190
+
191
+ addFloor() {
192
+ let floor = Model.compile({
193
+ type: 'floor',
194
+ fillStyle: 'gray',
195
+ top: 0,
196
+ left: 0,
197
+ width: 100,
198
+ height: 100
199
+ })
200
+
201
+ this.addComponent(floor)
202
+ this.activeIndex = this.components.length - 1
203
+ }
204
+
205
+ // drawLocationMarkers(locations) {
206
+ // let floor = this.activeFloor
207
+ // floor.drawLocationMarkers(locations)
208
+ // }
209
+ }
210
+
211
+ Component.register('indoor-map', IndoorMap)