@operato/scene-indoor-map 0.0.16

Sign up to get free protection for your applications and to get access to all the features.
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)