@pirireis/webglobeplugins 0.8.15-alpha → 0.8.18
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/Math/arc.ts +239 -0
- package/Math/bounds/line-bbox.js +225 -0
- package/Math/constants.ts +8 -0
- package/Math/methodology/arc-part-on-screen.ts +47 -0
- package/Math/methods.js +1 -3
- package/Math/plane.ts +167 -0
- package/Math/quaternion.ts +159 -0
- package/Math/ray.ts +101 -0
- package/Math/roadmap.md +10 -0
- package/Math/types.ts +36 -0
- package/Math/utils.js +3 -0
- package/Math/vector3d.ts +230 -0
- package/jest.config.js +6 -0
- package/package.json +1 -1
- package/point-heat-map/plugin-webworker.js +1 -1
- package/programs/line-on-globe/circle-accurate-flat.js +18 -3
- package/rangerings/rangeringangletext.js +2 -1
- package/tests/Math/arc.test.ts +52 -0
- package/tests/Math/plane.test.ts +45 -0
- package/tests/Math/quaternion.test.ts +98 -0
- package/tests/Math/ray-plane.test.ts +176 -0
- package/tests/Math/vector3d.test.ts +71 -0
- package/util/geometry/index.js +8 -5
- package/util/surface-line-data/arc-bboxes.ts +42 -0
- package/util/surface-line-data/arcs-to-cuts.js +74 -0
- package/util/surface-line-data/flow.ts +52 -0
- package/util/surface-line-data/rbush-manager.js +0 -0
- package/util/surface-line-data/types.ts +27 -0
- package/util/surface-line-data/web-worker.js +0 -0
- package/waveparticles/plugin.js +9 -6
- package/webworker-test/index.js +0 -10
- package/webworker-test/module.js +0 -10
- package/webworker-test/worker.js +0 -8
- /package/{webpack.config.js → util/surface-line-data/cut-arc.js} +0 -0
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PointHeatmapFlow } from "./point-to-heat-map-flow";
|
|
2
|
-
// import { webworkerStr } from "../util/interpolation/timetrack/web-worker-str";
|
|
2
|
+
// import { webworkerStr } from "../util/interpolation/timetrack/web-worker-str"; // TODO: Delete When this plugin is tested on production
|
|
3
3
|
import { createWorker } from "../util/interpolation/timetrack/index";
|
|
4
4
|
import { createTexture, getColorRampModed } from "../util";
|
|
5
5
|
import { opacityCheck, constraintFloat } from "../util/check/typecheck";
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
mercatorXYToGLPosition, POLE
|
|
7
7
|
} from "../../util/shaderfunctions/geometrytransformations";
|
|
8
8
|
|
|
9
|
+
const Pole = 20037508.34;
|
|
9
10
|
/**
|
|
10
11
|
* Warning:
|
|
11
12
|
* Insert the points from the second index and skip 1 point as you placed 361 points
|
|
@@ -204,9 +205,20 @@ function centerCoords2dflatDataCreator(globe, centerLong, centerLat, targetLong,
|
|
|
204
205
|
360 / (edgeCount - 2), // 1 for return to start point, 1 for cutting circles
|
|
205
206
|
);
|
|
206
207
|
|
|
208
|
+
let lastlg = pointsLongLat[0].long;
|
|
209
|
+
let lastlt = pointsLongLat[0].lat;
|
|
210
|
+
|
|
207
211
|
for (let i = 1; i < edgeCount; i++) {
|
|
208
212
|
const { long: lg, lat: lt } = pointsLongLat[i - 1];
|
|
209
|
-
|
|
213
|
+
if (manhattanDistance(lastlg, lastlt, lg, lt) < 170) {
|
|
214
|
+
centerCoords2dflat.set(
|
|
215
|
+
globe.api_GetMercator2DPoint(lg, lt)
|
|
216
|
+
, i * 2);
|
|
217
|
+
} else {
|
|
218
|
+
centerCoords2dflat.set([NaN, NaN], i * 2);
|
|
219
|
+
}
|
|
220
|
+
lastlg = lg;
|
|
221
|
+
lastlt = lt;
|
|
210
222
|
}
|
|
211
223
|
return centerCoords2dflat;
|
|
212
224
|
}
|
|
@@ -227,9 +239,11 @@ function centerCoords2dflatDataCreatorWithRadius(globe, centerLong, centerLat, r
|
|
|
227
239
|
for (let i = 1; i < edgeCount; i++) {
|
|
228
240
|
const { long: lg, lat: lt } = pointsLongLat[i - 1];
|
|
229
241
|
if (manhattanDistance(lastlg, lastlt, lg, lt) < 170) {
|
|
230
|
-
centerCoords2dflat.set(
|
|
242
|
+
centerCoords2dflat.set(
|
|
243
|
+
globe.api_GetMercator2DPoint(lg, lt)
|
|
244
|
+
, i * 2);
|
|
231
245
|
} else {
|
|
232
|
-
centerCoords2dflat.set([
|
|
246
|
+
centerCoords2dflat.set([NaN, NaN], i * 2);
|
|
233
247
|
}
|
|
234
248
|
lastlg = lg;
|
|
235
249
|
lastlt = lt;
|
|
@@ -242,6 +256,7 @@ function manhattanDistance(x1, y1, x2, y2) {
|
|
|
242
256
|
return Math.max(Math.abs(x1 - x2), Math.abs(y1 - y2));
|
|
243
257
|
}
|
|
244
258
|
|
|
259
|
+
|
|
245
260
|
export {
|
|
246
261
|
CircleCache, EDGE_COUNT, centerCoords2dflatDataCreator, centerCoords2dflatDataCreatorWithRadius
|
|
247
262
|
}
|
|
@@ -48,6 +48,7 @@ export default class RangeRingAngleText {
|
|
|
48
48
|
label.vAlignment = 2;
|
|
49
49
|
label.hAlignment = 2;
|
|
50
50
|
label.size = 17;
|
|
51
|
+
// eslint-disable-next-line
|
|
51
52
|
label.text = "`${aci}`"
|
|
52
53
|
|
|
53
54
|
return style;
|
|
@@ -163,7 +164,7 @@ export default class RangeRingAngleText {
|
|
|
163
164
|
|
|
164
165
|
/**
|
|
165
166
|
*
|
|
166
|
-
* @param {Array<{centerID:string, hide: bool}} centerHides
|
|
167
|
+
* @param {Array<{centerID:string, hide: bool}>} centerHides
|
|
167
168
|
*/
|
|
168
169
|
updateCentersHide(centerHides) {
|
|
169
170
|
if (this._hideAll) {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Arc } from '../../Math/arc';
|
|
2
|
+
import { Vector3D } from '../../Math/vector3d';
|
|
3
|
+
import { Quaternion } from '../../Math/quaternion';
|
|
4
|
+
import { Plane } from '../../Math/plane';
|
|
5
|
+
|
|
6
|
+
let _0vector: Vector3D;
|
|
7
|
+
let _1vector: Vector3D;
|
|
8
|
+
let _0arc: Arc;
|
|
9
|
+
let _1arc: Arc;
|
|
10
|
+
let _0quaternion: Quaternion;
|
|
11
|
+
let _medium: Plane;
|
|
12
|
+
|
|
13
|
+
beforeAll(() => {
|
|
14
|
+
|
|
15
|
+
_0vector = new Vector3D(0, 0, 0);
|
|
16
|
+
_1vector = new Vector3D(1, 1, 1).normalize();
|
|
17
|
+
_0arc = new Arc(_0vector, _1vector);
|
|
18
|
+
_1arc = new Arc(_0vector, _1vector);
|
|
19
|
+
_0quaternion = new Quaternion(0, 0, 0, 1);
|
|
20
|
+
_medium = new Plane(new Vector3D(0, 0, 1), 0);
|
|
21
|
+
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
test("intersectionMedium", () => {
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
_0quaternion.setFromAxisAngle(_0vector.set(1, 0, 0), -Math.PI / 3);
|
|
30
|
+
const A = _0vector.set(0, 1, 0).applyQuaternion(_0quaternion).clone();
|
|
31
|
+
_0quaternion.setFromAxisAngle(_0vector.set(1, 0, 0), Math.PI / 3);
|
|
32
|
+
const B = _0vector.set(0, 1, 0).applyQuaternion(_0quaternion).clone();
|
|
33
|
+
_0arc.setFromUnitVectors(A, B);
|
|
34
|
+
const _1arc = _0arc.intersectionMedium(_medium)!;
|
|
35
|
+
|
|
36
|
+
expect(_1arc !== null).toBeTruthy();
|
|
37
|
+
expect([A, B].some((v) => v.equals(_1arc.pointA) || v.equals(_1arc.pointB))).toBeTruthy();
|
|
38
|
+
expect([_1arc.pointA, _1arc.pointB].some((v) => v.equals(new Vector3D(0, 1, 0)))).toBeTruthy();
|
|
39
|
+
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
test("intersectionMedium 2 - single point arc", () => {
|
|
44
|
+
_0arc.setFromUnitVectors(new Vector3D(0, 1, 0), new Vector3D(0, 1, 0));
|
|
45
|
+
const result = _0arc.intersectionMedium(_medium);
|
|
46
|
+
expect(result).toBeNull();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
test(" populatePoints3D", () => {
|
|
51
|
+
|
|
52
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Plane } from '../../Math/plane';
|
|
2
|
+
import { Vector3D } from '../../Math/vector3d';
|
|
3
|
+
import { Ray } from '../../Math/ray';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
const _0vector: Vector3D = new Vector3D(0, 0, 1);
|
|
7
|
+
const _1vector: Vector3D = new Vector3D(0, 0, 1);
|
|
8
|
+
const _3vector: Vector3D = new Vector3D(0, 0, 1);
|
|
9
|
+
const _0ray: Ray = new Ray(new Vector3D(0, 0, 0), new Vector3D(1, 0, 0));;
|
|
10
|
+
const _0plane: Plane = new Plane(new Vector3D(1, 0, 0), 0);;
|
|
11
|
+
const _1plane: Plane = new Plane(new Vector3D(1, 0, 0), 0);;
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
beforeAll(() => {
|
|
15
|
+
_0vector.randomUnit();
|
|
16
|
+
_1vector.randomUnit();
|
|
17
|
+
_3vector.randomUnit();
|
|
18
|
+
_0plane.setFromPoints(_0vector, _1vector, _3vector);
|
|
19
|
+
_3vector.randomUnit();
|
|
20
|
+
_1plane.setFromPoints(_0vector, _1vector, _3vector);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
test("Plane Intersection => Ray", () => {
|
|
27
|
+
let result = Ray.fromTwoPlanes(_0plane, _1plane, _0ray);
|
|
28
|
+
expect(result).toBeInstanceOf(Ray);
|
|
29
|
+
if (result) {
|
|
30
|
+
expect(_0ray.contains(_0vector)).toBe(true);
|
|
31
|
+
expect(_0ray.contains(_1vector)).toBe(true);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("set from a normal and a point", () => {
|
|
36
|
+
const normal = new Vector3D(1, 0, 0);
|
|
37
|
+
const point = new Vector3D(1, 2, 3);
|
|
38
|
+
|
|
39
|
+
_0plane.setFromNormalAndPoint(normal, point);
|
|
40
|
+
expect(_0plane.normal.equals(normal)).toBe(true);
|
|
41
|
+
|
|
42
|
+
expect(_0plane.constant).toBe(point.dot(normal));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Quaternion } from '../../Math/quaternion';
|
|
2
|
+
import { Vector3D } from '../../Math/vector3d';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
let _0vector: Vector3D;
|
|
6
|
+
let _0quaternion: Quaternion;
|
|
7
|
+
let _1quaternion: Quaternion;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
beforeAll(() => {
|
|
11
|
+
_0vector = new Vector3D(0, 0, 0);
|
|
12
|
+
_0quaternion = new Quaternion(0, 0, 0, 1);
|
|
13
|
+
_1quaternion = new Quaternion(0, 0, 0, 1);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('Quaternion constructor and normalization', () => {
|
|
17
|
+
const quaternion = new Quaternion(1, 2, 3, 4);
|
|
18
|
+
expect(quaternion.x).toBe(1);
|
|
19
|
+
expect(quaternion.y).toBe(2);
|
|
20
|
+
expect(quaternion.z).toBe(3);
|
|
21
|
+
expect(quaternion.w).toBe(4);
|
|
22
|
+
|
|
23
|
+
const length = Math.sqrt(1 ** 2 + 2 ** 2 + 3 ** 2 + 4 ** 2);
|
|
24
|
+
expect(length).toBeCloseTo(length); // Check the length of the quaternion
|
|
25
|
+
|
|
26
|
+
quaternion.normalize();
|
|
27
|
+
expect(quaternion.x).toBeCloseTo(1 / length);
|
|
28
|
+
expect(quaternion.y).toBeCloseTo(2 / length);
|
|
29
|
+
expect(quaternion.z).toBeCloseTo(3 / length);
|
|
30
|
+
expect(quaternion.w).toBeCloseTo(4 / length);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
test('Quaternion Vector3D rotation , Z axis', () => {
|
|
35
|
+
|
|
36
|
+
const xyByEuler = (angle: number) => new Vector3D(
|
|
37
|
+
Math.cos(angle),
|
|
38
|
+
Math.sin(angle),
|
|
39
|
+
0
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
let angle = 0;
|
|
44
|
+
let step = Math.PI / 10;
|
|
45
|
+
_0quaternion.setFromAxisAngle(_0vector.set(0, 0, 1).clone(), step);
|
|
46
|
+
_0vector.set(1, 0, 0);
|
|
47
|
+
while (angle < Math.PI * 2) {
|
|
48
|
+
const vector = xyByEuler(angle);
|
|
49
|
+
expect(_0vector.equals(vector)).toBe(true);
|
|
50
|
+
angle += step;
|
|
51
|
+
_0vector.applyQuaternion(_0quaternion);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('Quaternion Vector3D rotation , Y axis', () => {
|
|
58
|
+
|
|
59
|
+
const xyByEuler = (angle: number) => new Vector3D(
|
|
60
|
+
Math.cos(angle),
|
|
61
|
+
0,
|
|
62
|
+
(angle <= Math.PI ? -1 : 1) * Math.abs(Math.sin(angle)) // because of right hand rule
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
let angle = 0;
|
|
67
|
+
let step = Math.PI / 10;
|
|
68
|
+
_0quaternion.setFromAxisAngle(_0vector.set(0, 1, 0).clone(), step);
|
|
69
|
+
_0vector.set(1, 0, 0);
|
|
70
|
+
while (angle < Math.PI * 2) {
|
|
71
|
+
const vector = xyByEuler(angle);
|
|
72
|
+
expect(_0vector.equals(vector)).toBe(true);
|
|
73
|
+
angle += step;
|
|
74
|
+
_0vector.applyQuaternion(_0quaternion);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
test('Quaternion Multiply', () => {
|
|
84
|
+
_0quaternion.setFromAxisAngle(_0vector.set(0, 0, 1), Math.PI / 2);
|
|
85
|
+
_1quaternion.setFromAxisAngle(_0vector.set(0, 1, 0), Math.PI / 2);
|
|
86
|
+
_0quaternion.multiply(_1quaternion);
|
|
87
|
+
_0vector.set(1, 0, 0).applyQuaternion(_0quaternion);
|
|
88
|
+
_0vector.equals(new Vector3D(0, -1, 0));
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
test("Quaternion all Zeros", () => {
|
|
93
|
+
|
|
94
|
+
_0quaternion.setFromAxisAngle(_0vector.set(0, 0, 0), 0);
|
|
95
|
+
_0vector.set(1, 0, 0).applyQuaternion(_0quaternion);
|
|
96
|
+
expect(_0vector.equals(new Vector3D(1, 0, 0))).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { Ray } from '../../Math/ray'
|
|
2
|
+
import { Vector3D } from '../../Math/vector3d';
|
|
3
|
+
import { Plane } from '../../Math/plane';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const _0vector: Vector3D = new Vector3D(0, 0, 1);
|
|
8
|
+
const _1vector: Vector3D = new Vector3D(0, 0, 1);
|
|
9
|
+
const _3vector: Vector3D = new Vector3D(0, 0, 1);
|
|
10
|
+
const _0ray: Ray = new Ray(new Vector3D(0, 0, 0), new Vector3D(1, 0, 0));;
|
|
11
|
+
const _0plane: Plane = new Plane(new Vector3D(1, 0, 0), 0);;
|
|
12
|
+
const _1plane: Plane = new Plane(new Vector3D(1, 0, 0), 0);;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
beforeAll(() => {
|
|
16
|
+
_0vector.randomUnit();
|
|
17
|
+
_1vector.randomUnit();
|
|
18
|
+
_3vector.randomUnit();
|
|
19
|
+
_0plane.setFromPoints(_0vector, _1vector, _3vector);
|
|
20
|
+
_3vector.randomUnit();
|
|
21
|
+
_1plane.setFromPoints(_0vector, _1vector, _3vector);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
test("Plane Intersection => Ray", () => {
|
|
27
|
+
let result = Ray.fromTwoPlanes(_0plane, _1plane, _0ray);
|
|
28
|
+
expect(result).toBeInstanceOf(Ray);
|
|
29
|
+
if (result) {
|
|
30
|
+
expect(_0ray.contains(_0vector)).toBe(true);
|
|
31
|
+
expect(_0ray.contains(_1vector)).toBe(true);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
test('Ray constructor', () => {
|
|
37
|
+
const origin = new Vector3D(1, 2, 3);
|
|
38
|
+
const direction = new Vector3D(4, 5, 6);
|
|
39
|
+
const ray = new Ray(origin, direction);
|
|
40
|
+
|
|
41
|
+
expect(ray.origin).toEqual(origin);
|
|
42
|
+
expect(ray.direction).toEqual(direction.normalize());
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('Ray from two planes 1', () => {
|
|
46
|
+
const plane1 = new Plane(new Vector3D(1, 0, 0), 5);
|
|
47
|
+
const plane2 = new Plane(new Vector3D(0, 1, 0), 10);
|
|
48
|
+
const ray = Ray.fromTwoPlanes(plane1, plane2)!;
|
|
49
|
+
const plane1copy = plane1.clone();
|
|
50
|
+
const plane2copy = plane2.clone();
|
|
51
|
+
expect(ray).toBeInstanceOf(Ray);
|
|
52
|
+
expect(ray.contains(new Vector3D(5, 10, 0))).toBe(true);
|
|
53
|
+
expect(ray.contains(new Vector3D(5, 10, 5))).toBe(true);
|
|
54
|
+
expect(ray.contains(new Vector3D(5, 10, -5))).toBe(true);
|
|
55
|
+
expect(plane1.equals(plane1copy)).toBe(true);
|
|
56
|
+
expect(plane2.equals(plane2copy)).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
test('Ray from two planes 2 ', () => {
|
|
61
|
+
const plane1 = new Plane(new Vector3D(1, 1, 1).normalize(), 1 / Math.sqrt(3));
|
|
62
|
+
const plane2 = new Plane(new Vector3D(0, 0, 1).normalize(), 0);
|
|
63
|
+
const plane1copy = plane1.clone();
|
|
64
|
+
const plane2copy = plane2.clone();
|
|
65
|
+
const ray = Ray.fromTwoPlanes(plane1, plane2)!;
|
|
66
|
+
expect(ray).toBeInstanceOf(Ray);
|
|
67
|
+
expect(ray.contains(new Vector3D(0.5, 0.5, 0))).toBe(true);
|
|
68
|
+
expect(ray.contains(new Vector3D(1, 0, 0))).toBe(true);
|
|
69
|
+
expect(ray.contains(new Vector3D(0, 1, 0))).toBe(true);
|
|
70
|
+
expect(plane1.equals(plane1copy)).toBe(true);
|
|
71
|
+
expect(plane2.equals(plane2copy)).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('Ray from two planes with parallel planes', () => {
|
|
75
|
+
test(`Ray from two planes from random points, try:`, () => {
|
|
76
|
+
for (let i = 0; i < 100; i++) {
|
|
77
|
+
_0vector.randomUnit();
|
|
78
|
+
_1vector.randomUnit();
|
|
79
|
+
_3vector.randomUnit();
|
|
80
|
+
_0plane.setFromPoints(_0vector, _1vector, _3vector);
|
|
81
|
+
|
|
82
|
+
const n = _3vector.clone().randomUnit();
|
|
83
|
+
_1plane.setFromPoints(_0vector, _1vector, n);
|
|
84
|
+
const plane1copy = _1plane.clone();
|
|
85
|
+
const plane2copy = _0plane.clone();
|
|
86
|
+
|
|
87
|
+
const ray = Ray.fromTwoPlanes(_0plane, _1plane)!;
|
|
88
|
+
if (ray === null) {
|
|
89
|
+
console.log(_0plane, _1plane, ray);
|
|
90
|
+
console.log(_0vector, _1vector, _3vector, n);
|
|
91
|
+
}
|
|
92
|
+
expect(ray).toBeInstanceOf(Ray);
|
|
93
|
+
|
|
94
|
+
expect(ray.contains(_0vector)).toBe(true);
|
|
95
|
+
expect(ray.contains(_1vector)).toBe(true);
|
|
96
|
+
expect(plane1copy.equals(_1plane)).toBe(true);
|
|
97
|
+
expect(plane2copy.equals(_0plane)).toBe(true);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
test('Ray intersection sphere', () => {
|
|
105
|
+
const ray = new Ray(new Vector3D(0, 0, 0), new Vector3D(0, 0, 1));
|
|
106
|
+
const sphereOrigin = new Vector3D(0, 0, 5);
|
|
107
|
+
const sphereRadius = 2;
|
|
108
|
+
const intersections = ray.intersectionSphere(sphereOrigin, sphereRadius)!;
|
|
109
|
+
|
|
110
|
+
expect(intersections).toBeInstanceOf(Array);
|
|
111
|
+
expect(intersections.length).toBe(2);
|
|
112
|
+
expect(intersections[0].equals(new Vector3D(0, 0, 3))).toBe(true);
|
|
113
|
+
expect(intersections[1].equals(new Vector3D(0, 0, 7))).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
describe('Ray from 2 points intersection with Sphere', () => {
|
|
119
|
+
test(`Ray from 2 points intersection with Sphere, try:`, () => {
|
|
120
|
+
for (let i = 0; i < 100; i++) {
|
|
121
|
+
const center = _3vector.randomUnit().scale(Math.random() * 10 + 1).clone();
|
|
122
|
+
const radius = Math.random() * 10 + 1;
|
|
123
|
+
const angle = Math.acos(_0vector.randomUnit().dot(_1vector.randomUnit()));
|
|
124
|
+
const _0clone = _0vector.scale(radius).add(center).clone();
|
|
125
|
+
const _1clone = _1vector.scale(radius).add(center).clone();
|
|
126
|
+
|
|
127
|
+
Ray.fromTwoPoints(_0vector, _1vector, _0ray);
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
const intersections = _0ray.intersectionSphere(center, radius)!;
|
|
131
|
+
expect(_3vector.equals(center)).toBe(true);
|
|
132
|
+
expect(intersections).toBeInstanceOf(Array);
|
|
133
|
+
|
|
134
|
+
expect(_0clone.equals(_0vector)).toBe(true);
|
|
135
|
+
expect(_1clone.equals(_1vector)).toBe(true);
|
|
136
|
+
|
|
137
|
+
expect(intersections.length).toBe(2);
|
|
138
|
+
|
|
139
|
+
_0vector.subtract(center).normalize().scale(radius).add(center);
|
|
140
|
+
_1vector.subtract(center).normalize().scale(radius).add(center);
|
|
141
|
+
|
|
142
|
+
expect(intersections.some((v) => v.equals(_0vector))).toBe(true);
|
|
143
|
+
expect(intersections.some((v) => v.equals(_1vector))).toBe(true);
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
const radius2 = radius * (1 + Math.random() * 10);
|
|
147
|
+
const intersections2 = _0ray.intersectionSphere(center, radius2)!;
|
|
148
|
+
expect(intersections2).toBeInstanceOf(Array);
|
|
149
|
+
expect(intersections2.length).toBe(2);
|
|
150
|
+
|
|
151
|
+
intersections2.forEach((v) => v.subtract(center).normalize());
|
|
152
|
+
|
|
153
|
+
const angle2 = Math.acos(intersections2[0].dot(intersections2[1]));
|
|
154
|
+
|
|
155
|
+
const x1 = angle / 2;
|
|
156
|
+
const D1 = Math.cos(x1) * radius;
|
|
157
|
+
const x2 = angle2 / 2;
|
|
158
|
+
const D2 = Math.cos(x2) * radius2;
|
|
159
|
+
|
|
160
|
+
expect(Math.abs(D1 - D2)).toBeLessThan(0.000001);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
test('Ray copy', () => {
|
|
168
|
+
const origin = new Vector3D(1, 2, 3);
|
|
169
|
+
const direction = new Vector3D(4, 5, 6);
|
|
170
|
+
const ray1 = new Ray(origin, direction);
|
|
171
|
+
const ray2 = new Ray(new Vector3D(), new Vector3D()).copy(ray1);
|
|
172
|
+
|
|
173
|
+
expect(ray2.origin.equals(origin)).toBe(true);
|
|
174
|
+
expect(ray2.direction.equals(direction)).toBe(true);
|
|
175
|
+
});
|
|
176
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Vector3D } from '../../Math/vector3d';
|
|
2
|
+
import { Quaternion } from '../../Math/quaternion';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const toRadians = (degrees: number) => degrees * (Math.PI / 180);
|
|
6
|
+
const toDegrees = (radians: number) => radians * (180 / Math.PI);
|
|
7
|
+
|
|
8
|
+
let _0vector: Vector3D;
|
|
9
|
+
let _1vector: Vector3D;
|
|
10
|
+
let _0quaternion: Quaternion;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
_0vector = new Vector3D(0, 0, 0);
|
|
14
|
+
_1vector = new Vector3D(1, 1, 1);
|
|
15
|
+
_0quaternion = new Quaternion(0, 0, 0, 1);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
test('add', () => {
|
|
20
|
+
expect(_0vector.add(_1vector).equals(new Vector3D(1, 1, 1))).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
test('setFromLonLat - toLonLat 2', () => {
|
|
25
|
+
expect(_0vector.setFromLonLat(0, 0).toLonLat()).toEqual({ lon: 0, lat: 0 });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
test(' applyQuaternion ', () => {
|
|
30
|
+
|
|
31
|
+
_0quaternion.setFromAxisAngle(new Vector3D(0, 0, 1), Math.PI / 2);
|
|
32
|
+
_0vector.setFromLonLat(toRadians(10), 0);
|
|
33
|
+
_0vector.applyQuaternion(_0quaternion);
|
|
34
|
+
const result = _0vector.toLonLat();
|
|
35
|
+
expect(result).toEqual({ lon: toRadians(100), lat: 0 });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
test('setFromLonLat - toLonLat', () => {
|
|
40
|
+
const result = _0vector.setFromLonLat(0, 0).toLonLat();
|
|
41
|
+
expect(result.lon).toBeCloseTo(0, 1e-6);
|
|
42
|
+
expect(result.lat).toBeCloseTo(0, 1e-6);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
test('scale', () => {
|
|
47
|
+
const scale = 2;
|
|
48
|
+
const result = _0vector.set(1, 2, 3).scale(scale);
|
|
49
|
+
expect(result.equals(new Vector3D(2, 4, 6))).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
test('negate', () => {
|
|
55
|
+
_0vector.set(1, -2, 3).negate();
|
|
56
|
+
expect(_0vector.equals(new Vector3D(-1, 2, -3))).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
test('cross same and negated', () => {
|
|
62
|
+
|
|
63
|
+
const negated = _0vector.randomUnit().clone().negate();
|
|
64
|
+
const result = _0vector.clone().cross(negated);
|
|
65
|
+
const resul2 = _0vector.clone().cross(_0vector);
|
|
66
|
+
|
|
67
|
+
expect(result.equals(new Vector3D(0, 0, 0))).toBe(true);
|
|
68
|
+
expect(resul2.equals(new Vector3D(0, 0, 0))).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
|
package/util/geometry/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
function latLongToPixelXY(latitude, longitude) {
|
|
2
2
|
return {
|
|
3
3
|
x: (longitude + 180) / 360,
|
|
4
4
|
y: (90 - latitude) / 180
|
|
@@ -11,7 +11,7 @@ export function latLongToPixelXY(latitude, longitude) {
|
|
|
11
11
|
* for example: Transform image coordinates to map coordinates
|
|
12
12
|
* glsl`vec3 pos = u_bbox_matrix * vec3(v_particle_pos.x, v_particle_pos.y, 1.0);`
|
|
13
13
|
*/
|
|
14
|
-
|
|
14
|
+
function createBBoxMatrix(minX, maxX, minY, maxY) {
|
|
15
15
|
return new Float32Array([
|
|
16
16
|
maxX - minX, 0, 0,
|
|
17
17
|
0, maxY - minY, 0,
|
|
@@ -19,7 +19,7 @@ export function createBBoxMatrix(minX, maxX, minY, maxY) {
|
|
|
19
19
|
])
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
function latLongBboxtoPixelXYBbox(minX, minY, maxX, maxY) {
|
|
23
23
|
const minXY = latLongToPixelXY(minY, minX);
|
|
24
24
|
const maxXY = latLongToPixelXY(maxY, maxX);
|
|
25
25
|
return createBBoxMatrix(minXY.x, maxXY.x, minXY.y, maxXY.y);
|
|
@@ -31,7 +31,7 @@ export function latLongBboxtoPixelXYBbox(minX, minY, maxX, maxY) {
|
|
|
31
31
|
* @param {Float32List} array
|
|
32
32
|
* @returns {Float32Array}
|
|
33
33
|
*/
|
|
34
|
-
|
|
34
|
+
function normalize(array, newLength = 1) {
|
|
35
35
|
let total = 0;
|
|
36
36
|
for (let i = 0; i < array.length; i++) {
|
|
37
37
|
total += array[i] * array[i];
|
|
@@ -45,6 +45,9 @@ export function normalize(array, newLength = 1) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
function sphereCoord(long, lat, globe, height = 0) {
|
|
49
49
|
return normalize(globe.api_GetCartesian3DPoint(long, lat, 0, 0), 6378.137 + height);
|
|
50
50
|
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
export { createBBoxMatrix, latLongToPixelXY, latLongBboxtoPixelXYBbox, sphereCoord, normalize };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
function naiveBBox(a: number[], b: number[]): BBox {
|
|
4
|
+
return {
|
|
5
|
+
minX: Math.min(a[0], b[0]),
|
|
6
|
+
minY: Math.min(a[1], b[1]),
|
|
7
|
+
maxX: Math.max(a[0], b[0]),
|
|
8
|
+
maxY: Math.max(a[1], b[1])
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
function slerp(a: Vector, b: Vector, t: number, theta: number, sinTheta: number): Vector {
|
|
15
|
+
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
function getArcBBoxes(arc: Arc, dotStep: number = 0.025): BBox[] {
|
|
20
|
+
const start = arc.start;
|
|
21
|
+
const end = arc.end;
|
|
22
|
+
const a = [start[0], start[1], start[2]];
|
|
23
|
+
const b = [end[0], end[1], end[2]];
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
const theta = Math.acos(a[0] * b[0] + a[1] * b[1] + a[2] * b[2]);
|
|
29
|
+
const step = dotStep / theta;
|
|
30
|
+
|
|
31
|
+
if (step >= 1) {
|
|
32
|
+
return [naiveBBox(start, end)];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
let points = [start] as Vector[];
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
let currentStep =
|
|
40
|
+
|
|
41
|
+
return bbox;
|
|
42
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ArcsToCutsManager {
|
|
6
|
+
|
|
7
|
+
constructor(rbush, { dotDistanceOfArcCuts = 0.01 }) {
|
|
8
|
+
|
|
9
|
+
this._map = new Map();
|
|
10
|
+
this._dotDistanceOfArcCuts = dotDistanceOfArcCuts;
|
|
11
|
+
this._rbush = rbush;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
insertArcs(arc) {
|
|
17
|
+
|
|
18
|
+
if (this._map.has(arc.id)) {
|
|
19
|
+
this._deleteCuts(arc.id);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
_deleteCuts(id) {
|
|
26
|
+
const cuts = this._map.get(id);
|
|
27
|
+
if (cuts) {
|
|
28
|
+
cuts.forEach(cut => cut.remove());
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
_cutArcAndAddToRBush(arc) {
|
|
34
|
+
const cuts = this._cutArcToBBoxs(arc);
|
|
35
|
+
cuts.forEach(cut => {
|
|
36
|
+
this._rbush.insert(cut);
|
|
37
|
+
});
|
|
38
|
+
this._map.set(arc.id, cuts);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
_cutArcToBBoxs(arc) {
|
|
43
|
+
const cuts = [];
|
|
44
|
+
|
|
45
|
+
// Calculate the bounding box of the arc
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
// Create a cut object and add it to the cuts array
|
|
49
|
+
const cut = {
|
|
50
|
+
minX: bbox.minX,
|
|
51
|
+
minY: bbox.minY,
|
|
52
|
+
maxX: bbox.maxX,
|
|
53
|
+
maxY: bbox.maxY,
|
|
54
|
+
arcId: arc.id,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
cuts.push(cut);
|
|
58
|
+
return cuts;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
function sphericalLinearInterpolation(phi1, lambda1, phi2, lambda2, t) {
|
|
65
|
+
// Spherical linear interpolation between two points on a sphere
|
|
66
|
+
// Args: phi1, lambda1: starting point (in radians)
|
|
67
|
+
// phi2, lambda2: ending point (in radians)
|
|
68
|
+
// t: interpolation parameter (0 <= t <= 1)
|
|
69
|
+
// Returns: [phi, lambda] in radians
|
|
70
|
+
|
|
71
|
+
const phi = phi1 + t * (phi2 - phi1);
|
|
72
|
+
const lambda = lambda1 + t * (lambda2 - lambda1);
|
|
73
|
+
return [phi, lambda];
|
|
74
|
+
}
|