@flighthq/interaction 0.1.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/displayHitTests.d.ts +13 -0
- package/dist/displayHitTests.d.ts.map +1 -0
- package/dist/displayHitTests.js +38 -0
- package/dist/displayHitTests.js.map +1 -0
- package/dist/displayObjectOverlap.d.ts +5 -0
- package/dist/displayObjectOverlap.d.ts.map +1 -0
- package/dist/displayObjectOverlap.js +21 -0
- package/dist/displayObjectOverlap.js.map +1 -0
- package/dist/hitTests.d.ts +38 -0
- package/dist/hitTests.d.ts.map +1 -0
- package/dist/hitTests.js +80 -0
- package/dist/hitTests.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/interactionManager.d.ts +38 -0
- package/dist/interactionManager.d.ts.map +1 -0
- package/dist/interactionManager.js +470 -0
- package/dist/interactionManager.js.map +1 -0
- package/dist/registerDefaultHitTestPoints.d.ts +2 -0
- package/dist/registerDefaultHitTestPoints.d.ts.map +1 -0
- package/dist/registerDefaultHitTestPoints.js +21 -0
- package/dist/registerDefaultHitTestPoints.js.map +1 -0
- package/dist/spatialQuery.d.ts +4 -0
- package/dist/spatialQuery.d.ts.map +1 -0
- package/dist/spatialQuery.js +37 -0
- package/dist/spatialQuery.js.map +1 -0
- package/dist/spriteHitTests.d.ts +5 -0
- package/dist/spriteHitTests.d.ts.map +1 -0
- package/dist/spriteHitTests.js +11 -0
- package/dist/spriteHitTests.js.map +1 -0
- package/package.json +43 -0
- package/src/displayHitTests.test.ts +137 -0
- package/src/displayObjectOverlap.test.ts +78 -0
- package/src/hitTests.test.ts +206 -0
- package/src/interactionManager.test.ts +635 -0
- package/src/registerDefaultHitTestPoints.test.ts +29 -0
- package/src/spatialQuery.test.ts +47 -0
- package/src/spriteHitTests.test.ts +76 -0
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flighthq/interaction",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"src/**/*.test.ts",
|
|
16
|
+
"!dist/**/*.test.js",
|
|
17
|
+
"!dist/**/*.test.d.ts",
|
|
18
|
+
"!dist/**/*.test.js.map",
|
|
19
|
+
"!dist/**/*.test.d.ts.map"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc -b",
|
|
23
|
+
"clean": "tsc -b --clean",
|
|
24
|
+
"test": "vitest run --config vitest.config.ts",
|
|
25
|
+
"test:watch": "vitest --watch --config vitest.config.ts",
|
|
26
|
+
"prepack": "npm run clean && npm run clean:dist && npm run build",
|
|
27
|
+
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@flighthq/geometry": "0.1.0",
|
|
31
|
+
"@flighthq/node": "0.1.0",
|
|
32
|
+
"@flighthq/displayobject": "0.1.0",
|
|
33
|
+
"@flighthq/signals": "0.1.0",
|
|
34
|
+
"@flighthq/types": "0.1.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@flighthq/shape": "*",
|
|
38
|
+
"@flighthq/sprite": "*",
|
|
39
|
+
"typescript": "^5.3.0"
|
|
40
|
+
},
|
|
41
|
+
"description": "Hit testing: point-in-node tests and object overlap detection",
|
|
42
|
+
"sideEffects": false
|
|
43
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { createDisplayObject } from '@flighthq/displayobject';
|
|
2
|
+
import { setRectangle } from '@flighthq/geometry';
|
|
3
|
+
import { getNodeLocalBoundsRectangle } from '@flighthq/node';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
defaultBitmapHitTestPointHandler,
|
|
7
|
+
defaultDisplayObjectHitTestPointHandler,
|
|
8
|
+
defaultHtmlViewHitTestPointHandler,
|
|
9
|
+
defaultMovieClipHitTestPointHandler,
|
|
10
|
+
defaultRenderViewHitTestPointHandler,
|
|
11
|
+
defaultRichTextHitTestPointHandler,
|
|
12
|
+
defaultShapeHitTestPointHandler,
|
|
13
|
+
defaultStageHitTestPointHandler,
|
|
14
|
+
defaultTextHitTestPointHandler,
|
|
15
|
+
defaultTextInputHitTestPointHandler,
|
|
16
|
+
defaultVideoHitTestPointHandler,
|
|
17
|
+
} from './displayHitTests';
|
|
18
|
+
|
|
19
|
+
function makeDisplayObject() {
|
|
20
|
+
const obj = createDisplayObject();
|
|
21
|
+
setRectangle(getNodeLocalBoundsRectangle(obj), 0, 0, 100, 100);
|
|
22
|
+
return obj;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe('defaultBitmapHitTestPointHandler', () => {
|
|
26
|
+
it('returns true when point is within local bounds', () => {
|
|
27
|
+
const obj = makeDisplayObject();
|
|
28
|
+
expect(defaultBitmapHitTestPointHandler(obj, 50, 50, false)).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('returns false when point is outside local bounds', () => {
|
|
32
|
+
const obj = makeDisplayObject();
|
|
33
|
+
expect(defaultBitmapHitTestPointHandler(obj, 200, 200, false)).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('defaultDisplayObjectHitTestPointHandler', () => {
|
|
38
|
+
it('always returns false — plain display objects have no hit geometry', () => {
|
|
39
|
+
const obj = makeDisplayObject();
|
|
40
|
+
expect(defaultDisplayObjectHitTestPointHandler(obj, 50, 50, false)).toBe(false);
|
|
41
|
+
expect(defaultDisplayObjectHitTestPointHandler(obj, 0, 0, false)).toBe(false);
|
|
42
|
+
expect(defaultDisplayObjectHitTestPointHandler(obj, 200, 200, false)).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('defaultHtmlViewHitTestPointHandler', () => {
|
|
47
|
+
it('always returns false — browser manages HtmlView hit testing', () => {
|
|
48
|
+
const obj = makeDisplayObject();
|
|
49
|
+
expect(defaultHtmlViewHitTestPointHandler(obj, 50, 50, false)).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('defaultMovieClipHitTestPointHandler', () => {
|
|
54
|
+
it('always returns false — containers have no self hit area', () => {
|
|
55
|
+
const obj = makeDisplayObject();
|
|
56
|
+
expect(defaultMovieClipHitTestPointHandler(obj, 50, 50, false)).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('defaultRenderViewHitTestPointHandler', () => {
|
|
61
|
+
it('returns true when point is within local bounds', () => {
|
|
62
|
+
const obj = makeDisplayObject();
|
|
63
|
+
expect(defaultRenderViewHitTestPointHandler(obj, 50, 50, false)).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('returns false when point is outside local bounds', () => {
|
|
67
|
+
const obj = makeDisplayObject();
|
|
68
|
+
expect(defaultRenderViewHitTestPointHandler(obj, 200, 200, false)).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('defaultRichTextHitTestPointHandler', () => {
|
|
73
|
+
it('returns true when point is within local bounds', () => {
|
|
74
|
+
const obj = makeDisplayObject();
|
|
75
|
+
expect(defaultRichTextHitTestPointHandler(obj, 50, 50, false)).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('returns false when point is outside local bounds', () => {
|
|
79
|
+
const obj = makeDisplayObject();
|
|
80
|
+
expect(defaultRichTextHitTestPointHandler(obj, 200, 200, false)).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('defaultShapeHitTestPointHandler', () => {
|
|
85
|
+
it('returns true when point is within local bounds', () => {
|
|
86
|
+
const obj = makeDisplayObject();
|
|
87
|
+
expect(defaultShapeHitTestPointHandler(obj, 50, 50, false)).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('returns false when point is outside local bounds', () => {
|
|
91
|
+
const obj = makeDisplayObject();
|
|
92
|
+
expect(defaultShapeHitTestPointHandler(obj, 200, 200, false)).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('defaultStageHitTestPointHandler', () => {
|
|
97
|
+
it('always returns false — containers have no self hit area', () => {
|
|
98
|
+
const obj = makeDisplayObject();
|
|
99
|
+
expect(defaultStageHitTestPointHandler(obj, 50, 50, false)).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('defaultTextHitTestPointHandler', () => {
|
|
104
|
+
it('returns true when point is within local bounds', () => {
|
|
105
|
+
const obj = makeDisplayObject();
|
|
106
|
+
expect(defaultTextHitTestPointHandler(obj, 50, 50, false)).toBe(true);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('returns false when point is outside local bounds', () => {
|
|
110
|
+
const obj = makeDisplayObject();
|
|
111
|
+
expect(defaultTextHitTestPointHandler(obj, 200, 200, false)).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('defaultTextInputHitTestPointHandler', () => {
|
|
116
|
+
it('returns true when point is within local bounds', () => {
|
|
117
|
+
const obj = makeDisplayObject();
|
|
118
|
+
expect(defaultTextInputHitTestPointHandler(obj, 50, 50, false)).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('returns false when point is outside local bounds', () => {
|
|
122
|
+
const obj = makeDisplayObject();
|
|
123
|
+
expect(defaultTextInputHitTestPointHandler(obj, 200, 200, false)).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('defaultVideoHitTestPointHandler', () => {
|
|
128
|
+
it('returns true when point is within local bounds', () => {
|
|
129
|
+
const obj = makeDisplayObject();
|
|
130
|
+
expect(defaultVideoHitTestPointHandler(obj, 50, 50, false)).toBe(true);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('returns false when point is outside local bounds', () => {
|
|
134
|
+
const obj = makeDisplayObject();
|
|
135
|
+
expect(defaultVideoHitTestPointHandler(obj, 200, 200, false)).toBe(false);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { createDisplayObject } from '@flighthq/displayobject';
|
|
2
|
+
import { createRectangle, setRectangle } from '@flighthq/geometry';
|
|
3
|
+
import { getNodeLocalBoundsRectangle } from '@flighthq/node';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
containsDisplayObject,
|
|
7
|
+
getDisplayObjectOverlapRectangle,
|
|
8
|
+
hitTestDisplayObjectsShape,
|
|
9
|
+
} from './displayObjectOverlap';
|
|
10
|
+
|
|
11
|
+
function makeAt(x: number, y: number, w: number, h: number) {
|
|
12
|
+
const obj = createDisplayObject();
|
|
13
|
+
setRectangle(getNodeLocalBoundsRectangle(obj), x, y, w, h);
|
|
14
|
+
return obj;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe('containsDisplayObject', () => {
|
|
18
|
+
it('returns true when inner is fully inside outer', () => {
|
|
19
|
+
const outer = makeAt(0, 0, 100, 100);
|
|
20
|
+
const inner = makeAt(10, 10, 20, 20);
|
|
21
|
+
expect(containsDisplayObject(outer, inner)).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('returns false when inner extends beyond outer', () => {
|
|
25
|
+
const outer = makeAt(0, 0, 50, 50);
|
|
26
|
+
const inner = makeAt(30, 30, 40, 40);
|
|
27
|
+
expect(containsDisplayObject(outer, inner)).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('returns false when inner is completely outside outer', () => {
|
|
31
|
+
const outer = makeAt(0, 0, 50, 50);
|
|
32
|
+
const inner = makeAt(100, 100, 20, 20);
|
|
33
|
+
expect(containsDisplayObject(outer, inner)).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('getDisplayObjectOverlapRectangle', () => {
|
|
38
|
+
it('writes the intersection rectangle into out', () => {
|
|
39
|
+
const a = makeAt(0, 0, 100, 100);
|
|
40
|
+
const b = makeAt(50, 50, 100, 100);
|
|
41
|
+
const out = createRectangle();
|
|
42
|
+
const result = getDisplayObjectOverlapRectangle(a, b, out);
|
|
43
|
+
expect(result).toBe(out);
|
|
44
|
+
expect(out.x).toBe(50);
|
|
45
|
+
expect(out.y).toBe(50);
|
|
46
|
+
expect(out.width).toBe(50);
|
|
47
|
+
expect(out.height).toBe(50);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('writes an empty rectangle when there is no overlap', () => {
|
|
51
|
+
const a = makeAt(0, 0, 50, 50);
|
|
52
|
+
const b = makeAt(100, 100, 50, 50);
|
|
53
|
+
const out = createRectangle();
|
|
54
|
+
getDisplayObjectOverlapRectangle(a, b, out);
|
|
55
|
+
expect(out.width).toBe(0);
|
|
56
|
+
expect(out.height).toBe(0);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('hitTestDisplayObjectsShape', () => {
|
|
61
|
+
it('returns true when centers overlap', () => {
|
|
62
|
+
const a = makeAt(0, 0, 100, 100);
|
|
63
|
+
const b = makeAt(20, 20, 60, 60);
|
|
64
|
+
expect(hitTestDisplayObjectsShape(a, b)).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('returns false when AABBs do not intersect', () => {
|
|
68
|
+
const a = makeAt(0, 0, 50, 50);
|
|
69
|
+
const b = makeAt(100, 100, 50, 50);
|
|
70
|
+
expect(hitTestDisplayObjectsShape(a, b)).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('returns false when AABBs barely intersect but no center is inside the other', () => {
|
|
74
|
+
const a = makeAt(0, 0, 10, 100);
|
|
75
|
+
const b = makeAt(8, 0, 10, 100);
|
|
76
|
+
expect(hitTestDisplayObjectsShape(a, b)).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { createDisplayObject, createDisplayObjectGeneric, getDisplayObjectRuntime } from '@flighthq/displayobject';
|
|
2
|
+
import { setRectangle } from '@flighthq/geometry';
|
|
3
|
+
import { addNodeChild, getNodeLocalBoundsRectangle, invalidateNodeLocalTransform } from '@flighthq/node';
|
|
4
|
+
import type { DisplayObject, DisplayObjectRuntime } from '@flighthq/types';
|
|
5
|
+
import { DisplayObjectKind } from '@flighthq/types';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
findGraphHitTarget,
|
|
9
|
+
hitTestDisplayObjects,
|
|
10
|
+
hitTestGraphLocalBounds,
|
|
11
|
+
hitTestGraphPoint,
|
|
12
|
+
registerHitTestPoint,
|
|
13
|
+
} from './hitTests';
|
|
14
|
+
|
|
15
|
+
describe('findGraphHitTarget', () => {
|
|
16
|
+
it('returns null when node is disabled', () => {
|
|
17
|
+
const obj = createDisplayObject();
|
|
18
|
+
obj.enabled = false;
|
|
19
|
+
expect(findGraphHitTarget(obj, 50, 50)).toBeNull();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('returns a child node registered with a hit handler', () => {
|
|
23
|
+
const parent = createDisplayObject();
|
|
24
|
+
const child = createDisplayObjectGeneric(DisplayObjectKind);
|
|
25
|
+
setRectangle(getNodeLocalBoundsRectangle(child), 0, 0, 100, 100);
|
|
26
|
+
addNodeChild(parent, child);
|
|
27
|
+
registerHitTestPoint(DisplayObjectKind, hitTestGraphLocalBounds);
|
|
28
|
+
const hit = findGraphHitTarget(parent, 50, 50);
|
|
29
|
+
expect(hit).toBe(child);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('hitTestDisplayObjects', () => {
|
|
34
|
+
let a: DisplayObject;
|
|
35
|
+
let b: DisplayObject;
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
a = createDisplayObject();
|
|
39
|
+
b = createDisplayObject();
|
|
40
|
+
|
|
41
|
+
addNodeChild(createDisplayObject(), a);
|
|
42
|
+
addNodeChild(createDisplayObject(), b);
|
|
43
|
+
|
|
44
|
+
setRectangle(getNodeLocalBoundsRectangle(a), 0, 0, 10, 10);
|
|
45
|
+
setRectangle(getNodeLocalBoundsRectangle(b), 0, 0, 10, 10);
|
|
46
|
+
|
|
47
|
+
a.x = 0;
|
|
48
|
+
a.y = 0;
|
|
49
|
+
b.x = 5;
|
|
50
|
+
b.y = 5;
|
|
51
|
+
|
|
52
|
+
a.scaleX = a.scaleY = 1;
|
|
53
|
+
b.scaleX = b.scaleY = 1;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('returns true when bounds intersect', () => {
|
|
57
|
+
const result = hitTestDisplayObjects(a, b);
|
|
58
|
+
expect(result).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('returns false when bounds do not intersect', () => {
|
|
62
|
+
b.x = 20;
|
|
63
|
+
b.y = 20;
|
|
64
|
+
invalidateNodeLocalTransform(b);
|
|
65
|
+
|
|
66
|
+
const result = hitTestDisplayObjects(a, b);
|
|
67
|
+
expect(result).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('returns false if either object has no parent', () => {
|
|
71
|
+
(getDisplayObjectRuntime(b) as DisplayObjectRuntime).parent = null;
|
|
72
|
+
|
|
73
|
+
const result = hitTestDisplayObjects(a, b);
|
|
74
|
+
expect(result).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('compares bounds in world space', () => {
|
|
78
|
+
a.scaleX = 1;
|
|
79
|
+
a.scaleY = 1;
|
|
80
|
+
|
|
81
|
+
b.x = 5;
|
|
82
|
+
b.y = 5;
|
|
83
|
+
invalidateNodeLocalTransform(b);
|
|
84
|
+
|
|
85
|
+
const result = hitTestDisplayObjects(a, b);
|
|
86
|
+
expect(result).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('includes child bounds when a child extends beyond the object local bounds', () => {
|
|
90
|
+
const child = createDisplayObject();
|
|
91
|
+
child.x = 90;
|
|
92
|
+
child.y = 90;
|
|
93
|
+
invalidateNodeLocalTransform(child);
|
|
94
|
+
setRectangle(getNodeLocalBoundsRectangle(child), 0, 0, 20, 20);
|
|
95
|
+
addNodeChild(a, child);
|
|
96
|
+
|
|
97
|
+
b.x = 100;
|
|
98
|
+
b.y = 100;
|
|
99
|
+
invalidateNodeLocalTransform(b);
|
|
100
|
+
|
|
101
|
+
expect(hitTestDisplayObjects(a, b)).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('hitTestGraphLocalBounds', () => {
|
|
106
|
+
it('returns true when world-space point is inside local bounds', () => {
|
|
107
|
+
const obj = createDisplayObject();
|
|
108
|
+
setRectangle(getNodeLocalBoundsRectangle(obj), 0, 0, 100, 100);
|
|
109
|
+
expect(hitTestGraphLocalBounds(obj, 50, 50)).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('returns false when world-space point is outside local bounds', () => {
|
|
113
|
+
const obj = createDisplayObject();
|
|
114
|
+
setRectangle(getNodeLocalBoundsRectangle(obj), 0, 0, 100, 100);
|
|
115
|
+
expect(hitTestGraphLocalBounds(obj, 200, 200)).toBe(false);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('hitTestGraphPoint', () => {
|
|
120
|
+
let obj: DisplayObject;
|
|
121
|
+
|
|
122
|
+
beforeAll(() => {
|
|
123
|
+
registerHitTestPoint(DisplayObjectKind, hitTestGraphLocalBounds);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
beforeEach(() => {
|
|
127
|
+
obj = createDisplayObject();
|
|
128
|
+
setRectangle(getNodeLocalBoundsRectangle(obj), 0, 0, 100, 100);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('returns true for point inside bounds', () => {
|
|
132
|
+
const result = hitTestGraphPoint(obj, 50, 50);
|
|
133
|
+
expect(result).toBe(true);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('returns false for point outside bounds', () => {
|
|
137
|
+
const result = hitTestGraphPoint(obj, 200, 200);
|
|
138
|
+
expect(result).toBe(false);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('returns false if object is not enabled', () => {
|
|
142
|
+
obj.enabled = false;
|
|
143
|
+
const result = hitTestGraphPoint(obj, 50, 50);
|
|
144
|
+
expect(result).toBe(false);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('returns false when point is outside bounds', () => {
|
|
148
|
+
const result = hitTestGraphPoint(obj, 200, 200);
|
|
149
|
+
expect(result).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('respects world transform', () => {
|
|
153
|
+
obj.x = 100;
|
|
154
|
+
obj.y = 100;
|
|
155
|
+
invalidateNodeLocalTransform(obj);
|
|
156
|
+
const inside = hitTestGraphPoint(obj, 150, 150);
|
|
157
|
+
const outside = hitTestGraphPoint(obj, 50, 50);
|
|
158
|
+
|
|
159
|
+
expect(inside).toBe(true);
|
|
160
|
+
expect(outside).toBe(false);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('works with the default shapeFlag param', () => {
|
|
164
|
+
const result = hitTestGraphPoint(obj, 50, 50);
|
|
165
|
+
expect(result).toBe(true);
|
|
166
|
+
|
|
167
|
+
const resultExplicit = hitTestGraphPoint(obj, 50, 50, true);
|
|
168
|
+
expect(resultExplicit).toBe(true);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('returns true when a child is hit even if the parent has no local bounds', () => {
|
|
172
|
+
const child = createDisplayObject();
|
|
173
|
+
setRectangle(getNodeLocalBoundsRectangle(child), 0, 0, 100, 100);
|
|
174
|
+
addNodeChild(obj, child);
|
|
175
|
+
|
|
176
|
+
setRectangle(getNodeLocalBoundsRectangle(obj), 0, 0, 0, 0);
|
|
177
|
+
expect(hitTestGraphPoint(obj, 50, 50)).toBe(true);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('does not test children of a disabled parent', () => {
|
|
181
|
+
obj.enabled = false;
|
|
182
|
+
|
|
183
|
+
const child = createDisplayObject();
|
|
184
|
+
setRectangle(getNodeLocalBoundsRectangle(child), 0, 0, 100, 100);
|
|
185
|
+
addNodeChild(obj, child);
|
|
186
|
+
|
|
187
|
+
expect(hitTestGraphPoint(obj, 50, 50)).toBe(false);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('uses a registered handler for a custom kind', () => {
|
|
191
|
+
const CustomKind = 'CustomKind';
|
|
192
|
+
registerHitTestPoint(CustomKind, () => true);
|
|
193
|
+
const custom = createDisplayObjectGeneric(CustomKind);
|
|
194
|
+
|
|
195
|
+
expect(hitTestGraphPoint(custom, 50, 50)).toBe(true);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe('registerHitTestPoint', () => {
|
|
200
|
+
it('registers a handler that hitTestGraphPoint will use', () => {
|
|
201
|
+
const kind = 'RegisterTest';
|
|
202
|
+
registerHitTestPoint(kind, () => true);
|
|
203
|
+
const node = createDisplayObjectGeneric(kind);
|
|
204
|
+
expect(hitTestGraphPoint(node, 0, 0)).toBe(true);
|
|
205
|
+
});
|
|
206
|
+
});
|