@pirireis/webglobeplugins 0.15.21-alpha → 0.15.22-alpha
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.js +1 -2
- package/Math/circle-cdf-points.js +1 -170
- package/Math/circle.js +0 -25
- package/Math/vec3.js +1 -1
- package/altitude-locator/plugin.js +1 -1
- package/package.json +1 -1
- package/point-tracks/plugin.js +1 -2
- package/programs/line-on-globe/lines-color-instanced-flat.js +0 -1
- package/programs/point-on-globe/element-globe-surface-glow.js +0 -1
- package/programs/totems/camerauniformblock.js +7 -0
- package/range-tools-on-terrain/bearing-line/plugin.js +0 -1
- package/range-tools-on-terrain/circle-line-chain/chain-list-map.js +4 -9
- package/range-tools-on-terrain/circle-line-chain/plugin.js +4 -2
- package/semiplugins/shape-on-terrain/padding-1-degree.js +94 -44
- package/Math/arc-generate-points copy.js +0 -366
- package/Math/globe-util/horizon-plane.js +0 -112
- package/altitude-locator/draw-subset-obj.js +0 -16
- package/semiplugins/shape-on-terrain/derived/padding-plugin.js +0 -101
package/Math/arc.js
CHANGED
|
@@ -143,8 +143,7 @@ function _populatePointsWithClosestPointInsideArc(out, arc, count, closestPoint)
|
|
|
143
143
|
* @param {Vec3} cameraPosition - The position of the camera.
|
|
144
144
|
* @param {number} quaternionReuseCount - The number of times to apply the same rotation before recalculating. Higher is faster but less accurate.
|
|
145
145
|
*/
|
|
146
|
-
function _distanceSampling(out, count, inArc, cameraPosition = vec3create(0, 0, 0),
|
|
147
|
-
quaternionReuseCount = 1) {
|
|
146
|
+
function _distanceSampling(out, count, inArc, cameraPosition = vec3create(0, 0, 0), quaternionReuseCount = 1) {
|
|
148
147
|
if (count === 0) {
|
|
149
148
|
return;
|
|
150
149
|
}
|
|
@@ -1,168 +1,4 @@
|
|
|
1
1
|
import { RADIANS } from "./methods";
|
|
2
|
-
function createTemplate(intervalCount, strength = 1.0) {
|
|
3
|
-
const out = new Array(intervalCount);
|
|
4
|
-
const TOTAL = 1;
|
|
5
|
-
let cummulative = 0;
|
|
6
|
-
if (strength <= 0) {
|
|
7
|
-
// If strength is 0, distribute evenly.
|
|
8
|
-
const step = TOTAL / (intervalCount - 1);
|
|
9
|
-
for (let i = 0; i < intervalCount; i++) {
|
|
10
|
-
out[i] = cummulative;
|
|
11
|
-
cummulative += step;
|
|
12
|
-
}
|
|
13
|
-
out[intervalCount - 1] = TOTAL; // Ensure the last value is exactly TOTAL
|
|
14
|
-
return out;
|
|
15
|
-
}
|
|
16
|
-
// If strength is greater than 0, bias towards the start.
|
|
17
|
-
const weights = new Array(intervalCount);
|
|
18
|
-
let currentStep = 1;
|
|
19
|
-
let totalWeight = 0;
|
|
20
|
-
// Calculate weights for each interval, decreasing for later intervals.
|
|
21
|
-
for (let i = 0; i < intervalCount; i++) {
|
|
22
|
-
// Using (i + 1) in the denominator gives earlier intervals the highest weight.
|
|
23
|
-
totalWeight += currentStep;
|
|
24
|
-
weights[i] = totalWeight;
|
|
25
|
-
currentStep += strength;
|
|
26
|
-
}
|
|
27
|
-
// Distribute the total angle based on the weights.
|
|
28
|
-
for (let i = 0; i < intervalCount; i++) {
|
|
29
|
-
// Round to the nearest whole number for this interval.
|
|
30
|
-
out[i] = (weights[i] / totalWeight) * TOTAL;
|
|
31
|
-
}
|
|
32
|
-
if (out[intervalCount - 1] !== TOTAL) {
|
|
33
|
-
out[intervalCount - 1] = TOTAL;
|
|
34
|
-
}
|
|
35
|
-
// console.log("createTemplate", "strength", strength, "intervalCount", intervalCount, "out", out);
|
|
36
|
-
return out;
|
|
37
|
-
}
|
|
38
|
-
function interpolation(a, b, t) {
|
|
39
|
-
return a + (b - a) * t;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Populates the `out` array with values from the `template` array,
|
|
43
|
-
* distributing the surplus length as interpolated values between the template samples.
|
|
44
|
-
*
|
|
45
|
-
* @param out The destination array to be filled. Its length must be >= template.length.
|
|
46
|
-
* @param template An ordered array of numbers to be placed in the `out` array.
|
|
47
|
-
* @param strength A parameter controlling the distribution bias of surplus samples.
|
|
48
|
-
* - strength = 0: Surplus samples are distributed evenly.
|
|
49
|
-
* - strength > 0: Surplus samples are biased towards the start of the template.
|
|
50
|
-
* - Higher values create a stronger bias.
|
|
51
|
-
*/
|
|
52
|
-
function increasePopulation(// TODO: THERE IS A BUG
|
|
53
|
-
out, template, strength = 1.0) {
|
|
54
|
-
const sourceLength = template.length;
|
|
55
|
-
const destinationLength = out.length;
|
|
56
|
-
const outLengthAtStart = out.length;
|
|
57
|
-
// --- Handle Edge Cases ---
|
|
58
|
-
// If the template is empty, there is nothing to interpolate from.
|
|
59
|
-
if (sourceLength === 0) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
// If the template has only one value, fill the output array with that value.
|
|
63
|
-
if (sourceLength === 1) {
|
|
64
|
-
out.fill(template[0]);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
// If the destination is not larger than the source, just copy the template values.
|
|
68
|
-
if (destinationLength <= sourceLength) {
|
|
69
|
-
for (let i = 0; i < destinationLength; i++) {
|
|
70
|
-
out[i] = template[i];
|
|
71
|
-
}
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
// --- Main Logic ---
|
|
75
|
-
const surplus = destinationLength - sourceLength;
|
|
76
|
-
const numGaps = sourceLength - 1;
|
|
77
|
-
// This array will hold the number of extra samples to add in each gap.
|
|
78
|
-
const surplusPerGap = new Array(numGaps).fill(0);
|
|
79
|
-
if (surplus > 0) {
|
|
80
|
-
// --- Step 1: Determine the distribution of surplus items ---
|
|
81
|
-
// If strength is 0, distribute surplus items as evenly as possible.
|
|
82
|
-
if (strength <= 0) {
|
|
83
|
-
const baseToAdd = Math.floor(surplus / numGaps);
|
|
84
|
-
const remainder = surplus % numGaps;
|
|
85
|
-
for (let i = 0; i < numGaps; i++) {
|
|
86
|
-
// Distribute the remainder one by one to the first gaps.
|
|
87
|
-
surplusPerGap[i] = baseToAdd + (i < remainder ? 1 : 0);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
// Biased Distribution: more items in earlier gaps.
|
|
92
|
-
const weights = new Array(numGaps);
|
|
93
|
-
let totalWeight = 0;
|
|
94
|
-
// A. Calculate a weight for each gap. The weight decreases for later gaps.
|
|
95
|
-
// The `strength` parameter makes this decay steeper.
|
|
96
|
-
for (let i = 0; i < numGaps; i++) {
|
|
97
|
-
// Using (i + 1) in the denominator gives earlier gaps (i=0) the highest weight.
|
|
98
|
-
const weight = Math.pow(1 / (i + 1), strength);
|
|
99
|
-
weights[i] = weight;
|
|
100
|
-
totalWeight += weight;
|
|
101
|
-
}
|
|
102
|
-
// B. Distribute the surplus based on the calculated weights.
|
|
103
|
-
// This method ensures the total distributed count equals the surplus.
|
|
104
|
-
let distributedCount = 0;
|
|
105
|
-
for (let i = 0; i < numGaps - 1; i++) {
|
|
106
|
-
const idealCount = (weights[i] / totalWeight) * surplus;
|
|
107
|
-
// Round to the nearest whole number for this gap.
|
|
108
|
-
const countForThisGap = Math.ceil(idealCount);
|
|
109
|
-
surplusPerGap[i] = countForThisGap;
|
|
110
|
-
distributedCount += countForThisGap;
|
|
111
|
-
}
|
|
112
|
-
// Assign the remainder to the last gap to guarantee the sum is correct.
|
|
113
|
-
//surplusPerGap[numGaps - 1] = surplus - distributedCount;
|
|
114
|
-
// add to first
|
|
115
|
-
const leftover = surplus - distributedCount;
|
|
116
|
-
if (leftover > 0) {
|
|
117
|
-
surplusPerGap[0] += leftover; // Add any leftover to the first gap.
|
|
118
|
-
}
|
|
119
|
-
console.log("leftover", leftover, "distributedCount", distributedCount, "surplus", surplus);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
// --- Step 2: Populate the `out` array ---
|
|
123
|
-
let outIndex = 0;
|
|
124
|
-
for (let i = 0; i < surplusPerGap.length; i++) {
|
|
125
|
-
if (typeof surplusPerGap[i] !== "number" || isNaN(surplusPerGap[i]) || !isFinite(surplusPerGap[i])) {
|
|
126
|
-
console.warn("increasePopulation: Invalid surplusPerGap value at index", i, ":", surplusPerGap[i]);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
for (let i = 0; i < sourceLength; i++) {
|
|
130
|
-
// A. Add the original template item.
|
|
131
|
-
out[outIndex++] = template[i];
|
|
132
|
-
// B. If this is not the last template item, fill the gap after it.
|
|
133
|
-
if (i < numGaps) {
|
|
134
|
-
const numToAdd = surplusPerGap[i];
|
|
135
|
-
if (numToAdd <= 0)
|
|
136
|
-
continue;
|
|
137
|
-
const startVal = template[i];
|
|
138
|
-
const endVal = template[i + 1];
|
|
139
|
-
// C. Add the interpolated ("surplus") items for this gap.
|
|
140
|
-
// The total number of intervals in this gap is numToAdd + 1.
|
|
141
|
-
const totalIntervals = numToAdd + 1;
|
|
142
|
-
for (let j = 1; j <= numToAdd; j++) {
|
|
143
|
-
const t = j / totalIntervals; // The interpolation factor (0 < t < 1)
|
|
144
|
-
// Linear interpolation: out = start * (1 - t) + end * t
|
|
145
|
-
const interpolatedValue = startVal * (1 - t) + endVal * t;
|
|
146
|
-
out[outIndex++] = interpolatedValue;
|
|
147
|
-
if (outIndex >= out.length) {
|
|
148
|
-
console.warn("increasePopulation: Output array overflow. Stopping early.");
|
|
149
|
-
console.warn("processeed item ratio:", i, "/", sourceLength);
|
|
150
|
-
let count = numToAdd - j;
|
|
151
|
-
console.log("count", count);
|
|
152
|
-
for (let _i = i; _i < sourceLength; _i++) {
|
|
153
|
-
count += surplusPerGap[_i];
|
|
154
|
-
}
|
|
155
|
-
console.warn("increasePopulation: Not enough space to add", count, "more items.");
|
|
156
|
-
return; // Prevent overflow if the output array is not large enough.
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
// --- Step 3: Ensure the output length is correct ---
|
|
162
|
-
if (outLengthAtStart !== out.length) {
|
|
163
|
-
console.warn("increasePopulation: Output length mismatch. Expected", outLengthAtStart, "but got", out.length);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
2
|
function createCummulativeTemplate(numberOfPoints, strength, denseRatio = 0.5 // Ratio of points to be densely packed at the start.
|
|
167
3
|
) {
|
|
168
4
|
// Handle edge cases for the number of points.
|
|
@@ -239,9 +75,4 @@ function globeFindPointByPolarHalfCircle(out, globe, centerLong, centerLat, radi
|
|
|
239
75
|
offset -= 2;
|
|
240
76
|
}
|
|
241
77
|
}
|
|
242
|
-
export {
|
|
243
|
-
// createTemplate,
|
|
244
|
-
// increasePopulation,
|
|
245
|
-
// interpolation,
|
|
246
|
-
// createCummulativeTemplate,
|
|
247
|
-
createCummulativeTemplateStash, globeFindPointByPolar, globeFindPointByPolarHalfCircle };
|
|
78
|
+
export { createCummulativeTemplateStash, globeFindPointByPolar, globeFindPointByPolarHalfCircle };
|
package/Math/circle.js
CHANGED
|
@@ -29,30 +29,5 @@ function createCircleClosestAzimuthAngleProperties(circle) {
|
|
|
29
29
|
normal: normal,
|
|
30
30
|
northPointProjectedToOriginPlaneNormalized: N,
|
|
31
31
|
};
|
|
32
|
-
// const distance = normal[2]; //dot(normal, [0, 0, 1] as Vec3)
|
|
33
|
-
// const N: Vec3 = [0, 0, 0];
|
|
34
|
-
// if (Math.abs(distance) < 1e-6) {
|
|
35
|
-
// N[0] = -1;
|
|
36
|
-
// N[1] = 0;
|
|
37
|
-
// N[2] = 0;
|
|
38
|
-
// } else {
|
|
39
|
-
// // TODO: add cases for 8 parts of the sphere
|
|
40
|
-
// copy(N, normal);
|
|
41
|
-
// multiplyScalar(N, N, distance);
|
|
42
|
-
// if (N[2] >= 0) {
|
|
43
|
-
// N[0] = -N[0];
|
|
44
|
-
// N[1] = -N[1];
|
|
45
|
-
// N[2] = 1 - N[2];
|
|
46
|
-
// } else {
|
|
47
|
-
// N[2] = -N[2];
|
|
48
|
-
// N[0] = -N[0];
|
|
49
|
-
// N[1] = -N[1];
|
|
50
|
-
// }
|
|
51
|
-
// }
|
|
52
|
-
// normalize(N, N);
|
|
53
|
-
// return {
|
|
54
|
-
// normal: normal,
|
|
55
|
-
// northPointProjectedToOriginPlaneNormalized: N,
|
|
56
|
-
// }
|
|
57
32
|
}
|
|
58
33
|
export { closestAzimuthAngle, createCircleClosestAzimuthAngleProperties };
|
package/Math/vec3.js
CHANGED
|
@@ -85,7 +85,7 @@ function equals(a, b) {
|
|
|
85
85
|
Math.abs(a[2] - b[2]) < EPSILON);
|
|
86
86
|
}
|
|
87
87
|
function fromUnitVectorToLongLat(out, a) {
|
|
88
|
-
const len = length(a);
|
|
88
|
+
const len = length(a);
|
|
89
89
|
if (len === 0) {
|
|
90
90
|
throw new Error('Cannot convert a zero vector to unit vector');
|
|
91
91
|
}
|
|
@@ -335,7 +335,7 @@ class PointGlowLineToEarthPlugin {
|
|
|
335
335
|
});
|
|
336
336
|
}
|
|
337
337
|
_doesChanged() {
|
|
338
|
-
return this.
|
|
338
|
+
return this.globe.api_IsScreenMoving();
|
|
339
339
|
}
|
|
340
340
|
_changed() {
|
|
341
341
|
this._parametersChanged = true;
|
package/package.json
CHANGED
package/point-tracks/plugin.js
CHANGED
|
@@ -144,7 +144,7 @@ class PointTracksPlugin {
|
|
|
144
144
|
* @returns
|
|
145
145
|
*/
|
|
146
146
|
insertBulk(tracks) {
|
|
147
|
-
this._fillTracksToPointsMap(tracks);
|
|
147
|
+
this._fillTracksToPointsMap(tracks);
|
|
148
148
|
const flattenedPoints = tracks.map(trackToFlatPoints, this._objectStoreExtraParametersFiltered).flat();
|
|
149
149
|
const currentLoad = this._bufferOrchestrator.length;
|
|
150
150
|
if (currentLoad + flattenedPoints.length >= 2147483647) {
|
|
@@ -224,7 +224,6 @@ class PointTracksPlugin {
|
|
|
224
224
|
return;
|
|
225
225
|
this._isFreed = true;
|
|
226
226
|
this._pickerDisplayer.free();
|
|
227
|
-
this._tracksToPointMap.clear();
|
|
228
227
|
PointOnGlobeProgramCache.release(this.globe);
|
|
229
228
|
this.gl.deleteBuffer(this._focusParams.elementBuffer);
|
|
230
229
|
this._bufferManagersMap.forEach(({ bufferManager, adaptor }) => bufferManager.free());
|
|
@@ -8,7 +8,6 @@ import { attributeLoader } from "../../util/gl-util/buffer/attribute-loader";
|
|
|
8
8
|
* This program draws line between points provided from the first and second buffer.
|
|
9
9
|
* draw method gl.drawArraysInstanced( gl.POINTS, 0,2, lineCount);
|
|
10
10
|
* */
|
|
11
|
-
// TODO: find better way to eleminate edge case distance(posA, posB) > 37000000.0
|
|
12
11
|
const vertexShaderSource = `#version 300 es
|
|
13
12
|
precision highp float;
|
|
14
13
|
${CameraUniformBlockString}
|
|
@@ -100,7 +100,6 @@ class Logic {
|
|
|
100
100
|
const vao = gl.createVertexArray();
|
|
101
101
|
const divisor = 1;
|
|
102
102
|
gl.bindVertexArray(vao);
|
|
103
|
-
// TODO constants to escape values
|
|
104
103
|
attributeLoader(gl, pos3DObj, 0, 3, { divisor });
|
|
105
104
|
attributeLoader(gl, radiusObj, 1, 1, { divisor, escapeValues: [-1] });
|
|
106
105
|
attributeLoader(gl, colorObj, 2, 4, { divisor, escapeValues: [-1, -1, -1, -1] });
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { globeProgramCache } from "../programcache";
|
|
2
2
|
import { fromLongLatToUnitVector } from "../../Math/vec3";
|
|
3
3
|
import { RADIANS } from "../../Math/methods";
|
|
4
|
+
import { GlobeChangeObserver } from "./globe-changes";
|
|
4
5
|
export const CameraUniformBlockString = `
|
|
5
6
|
layout(std140) uniform CameraUniformBlock {
|
|
6
7
|
mat4 view; // 64 bytes 0
|
|
@@ -29,6 +30,7 @@ export class CameraUniformBlockTotem {
|
|
|
29
30
|
_isMovedParams;
|
|
30
31
|
_normalizedCameraVector = [0, 0, 0]; // Normalized camera vector
|
|
31
32
|
// _frustumPlanes: any; // Uncomment and type if used
|
|
33
|
+
_globeChangeObserver = null;
|
|
32
34
|
constructor() {
|
|
33
35
|
this.id = "CameraUniformBlockTotem";
|
|
34
36
|
this.description =
|
|
@@ -51,6 +53,7 @@ export class CameraUniformBlockTotem {
|
|
|
51
53
|
this.ubo = this._createUBO();
|
|
52
54
|
this.traslateFloat32 = new Float32Array(3);
|
|
53
55
|
this.mapWHFloat32 = new Float32Array(2);
|
|
56
|
+
this._globeChangeObserver = new GlobeChangeObserver(globe);
|
|
54
57
|
this.setGeometry();
|
|
55
58
|
this.resize();
|
|
56
59
|
}
|
|
@@ -86,6 +89,7 @@ export class CameraUniformBlockTotem {
|
|
|
86
89
|
const ubo = this.ubo;
|
|
87
90
|
const mapWHFloat32 = this.mapWHFloat32;
|
|
88
91
|
const globe = this.globe;
|
|
92
|
+
this._globeChangeObserver?.checkChanges();
|
|
89
93
|
gl.bindBuffer(gl.UNIFORM_BUFFER, ubo);
|
|
90
94
|
{
|
|
91
95
|
// view, projection, translate
|
|
@@ -196,6 +200,9 @@ export class CameraUniformBlockTotem {
|
|
|
196
200
|
fromLongLatToUnitVector(result, [CenterLong * RADIANS, CenterLat * RADIANS]);
|
|
197
201
|
return result;
|
|
198
202
|
}
|
|
203
|
+
getGlobeChanges() {
|
|
204
|
+
return this._globeChangeObserver?.getChanges();
|
|
205
|
+
}
|
|
199
206
|
free() {
|
|
200
207
|
const gl = this.gl;
|
|
201
208
|
const ubo = this.ubo;
|
|
@@ -216,7 +216,6 @@ export class BearingLinePlugin {
|
|
|
216
216
|
this.drawOptions.drawAngleRing = drawAngleRing;
|
|
217
217
|
this.globe.DrawRender();
|
|
218
218
|
}
|
|
219
|
-
// TODO:
|
|
220
219
|
updatePartial(items, { textWriterIDs } = {}) {
|
|
221
220
|
if (this._freed) {
|
|
222
221
|
throw new Error("Plugin has been freed, cannot update item.");
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* set and get node data
|
|
3
|
-
* node indexes;
|
|
4
|
-
*/
|
|
5
1
|
export class ChainListMap {
|
|
6
2
|
_chainMap;
|
|
7
3
|
_chainSideProperties;
|
|
@@ -98,7 +94,7 @@ export class ChainListMap {
|
|
|
98
94
|
});
|
|
99
95
|
}
|
|
100
96
|
this._chainMap.delete(chainKey);
|
|
101
|
-
this._chainSideProperties.delete(chainKey);
|
|
97
|
+
this._chainSideProperties.delete(chainKey);
|
|
102
98
|
for (const key of keys)
|
|
103
99
|
this._indexMap.delete(key);
|
|
104
100
|
return keys;
|
|
@@ -177,10 +173,9 @@ export class ChainListMap {
|
|
|
177
173
|
memoryProperties[key] = value;
|
|
178
174
|
});
|
|
179
175
|
}
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
176
|
+
// getNodeProperties(chainKey: string): ChainProperties | undefined {
|
|
177
|
+
// return this._chainSideProperties.get(chainKey);
|
|
178
|
+
// }
|
|
184
179
|
getChainProperties(chainKey) {
|
|
185
180
|
return this._chainSideProperties.get(chainKey);
|
|
186
181
|
}
|
|
@@ -158,12 +158,14 @@ export class CircleLineChainPlugin {
|
|
|
158
158
|
this.globe?.DrawRender();
|
|
159
159
|
}
|
|
160
160
|
setOpacities(opacities) {
|
|
161
|
-
|
|
161
|
+
if (this._freed) {
|
|
162
|
+
console.warn("CircleLineChainPlugin is freed, cannot set opacities.");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
162
165
|
this._opacities = {
|
|
163
166
|
...this._opacities,
|
|
164
167
|
...opacities
|
|
165
168
|
};
|
|
166
|
-
// set Opacities of plugins
|
|
167
169
|
this.globe?.DrawRender();
|
|
168
170
|
}
|
|
169
171
|
setElevationMode(mode) {
|
|
@@ -24,15 +24,14 @@ export class Padding1DegreePlugin {
|
|
|
24
24
|
bufferOrchestrator;
|
|
25
25
|
bufferManagersMap;
|
|
26
26
|
lineProgram = null;
|
|
27
|
-
_opacityLODCoef = 1;
|
|
28
|
-
_pluginOpacity = 1; // this is used to set the opacity of the plugin
|
|
29
27
|
_innerPaddingRatio = 0.9; //
|
|
30
28
|
_float32Array = null; // this is used to forward the data to the buffer manager
|
|
31
29
|
_uboHandler = null; // this is used to forward the data to the shader program
|
|
32
30
|
_bufferNames = ["position2d", "position3d"];
|
|
33
31
|
_vao = null; // this is used to store the VAO for the plugin
|
|
34
32
|
_freed = false; // this is used to check if the plugin is freed or not
|
|
35
|
-
|
|
33
|
+
_userOpacity = 1;
|
|
34
|
+
_adaptiveOpacityMultiplier = 1;
|
|
36
35
|
_dataMap = new Map();
|
|
37
36
|
_options = {
|
|
38
37
|
bufferType: "STATIC_DRAW",
|
|
@@ -86,7 +85,7 @@ export class Padding1DegreePlugin {
|
|
|
86
85
|
this.bufferOrchestrator.deleteBulk(keys, this.bufferManagersMap);
|
|
87
86
|
this.globe?.DrawRender();
|
|
88
87
|
}
|
|
89
|
-
setPluginOpacity(opacity) {
|
|
88
|
+
setPluginOpacity(opacity, drawRender = false) {
|
|
90
89
|
if (this._freed) {
|
|
91
90
|
console.warn("Plugin is freed, cannot set opacity");
|
|
92
91
|
return;
|
|
@@ -96,9 +95,10 @@ export class Padding1DegreePlugin {
|
|
|
96
95
|
return;
|
|
97
96
|
}
|
|
98
97
|
opacityCheck(opacity);
|
|
99
|
-
this.
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
this._userOpacity = opacity;
|
|
99
|
+
if (drawRender) {
|
|
100
|
+
this.globe.DrawRender();
|
|
101
|
+
}
|
|
102
102
|
}
|
|
103
103
|
setDefaultColor(color) {
|
|
104
104
|
if (this._freed) {
|
|
@@ -143,19 +143,19 @@ export class Padding1DegreePlugin {
|
|
|
143
143
|
this._float32Array.fill(NaN); // reset the float32Array
|
|
144
144
|
}
|
|
145
145
|
for (let i = 0; i < 360; i++) {
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
if (
|
|
146
|
+
const outerCoords = paddingInput.outerCoords[i];
|
|
147
|
+
const innerCoords = paddingInput.innerCoords[i];
|
|
148
|
+
if (outerCoords === null || innerCoords === null) {
|
|
149
149
|
continue;
|
|
150
150
|
}
|
|
151
|
-
let coords = globe.api_GetMercator2DPoint(
|
|
151
|
+
let coords = globe.api_GetMercator2DPoint(outerCoords[0], outerCoords[1]);
|
|
152
152
|
// fill the second coordinate with 0
|
|
153
153
|
this._float32Array[i * 6] = coords[0];
|
|
154
154
|
this._float32Array[i * 6 + 1] = coords[1];
|
|
155
|
-
let
|
|
155
|
+
let coordsInner = globe.api_GetMercator2DPoint(innerCoords[0], innerCoords[1]);
|
|
156
156
|
// fill the second coordinate with 0
|
|
157
|
-
this._float32Array[i * 6 + 2] =
|
|
158
|
-
this._float32Array[i * 6 + 3] =
|
|
157
|
+
this._float32Array[i * 6 + 2] = coordsInner[0];
|
|
158
|
+
this._float32Array[i * 6 + 3] = coordsInner[1];
|
|
159
159
|
}
|
|
160
160
|
return this._float32Array.subarray(0, 360 * 2 * 3);
|
|
161
161
|
}
|
|
@@ -174,22 +174,22 @@ export class Padding1DegreePlugin {
|
|
|
174
174
|
}
|
|
175
175
|
const _float32Array = this._float32Array;
|
|
176
176
|
for (let i = 0; i < 360; i++) {
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
if (
|
|
177
|
+
const outerCoords = paddingInput.outerCoords[i];
|
|
178
|
+
const innerCoords = paddingInput.innerCoords[i];
|
|
179
|
+
if (outerCoords === null || innerCoords === null) {
|
|
180
180
|
continue;
|
|
181
181
|
}
|
|
182
182
|
const height = paddingInput.heightFromGroundIn3D ?? this._options.defaultHeightFromGroundIn3D;
|
|
183
|
-
let coords = globe.api_GetCartesian3DPoint(
|
|
183
|
+
let coords = globe.api_GetCartesian3DPoint(outerCoords[0], outerCoords[1], height, this._options.isMSL);
|
|
184
184
|
// fill the second coordinate with 0
|
|
185
185
|
_float32Array[i * 9] = coords[0];
|
|
186
186
|
_float32Array[i * 9 + 1] = coords[1];
|
|
187
187
|
_float32Array[i * 9 + 2] = coords[2];
|
|
188
|
-
let
|
|
188
|
+
let coordsInner = globe.api_GetCartesian3DPoint(innerCoords[0], innerCoords[1], height, this._options.isMSL);
|
|
189
189
|
// fill the second coordinate with 0
|
|
190
|
-
_float32Array[i * 9 + 3] =
|
|
191
|
-
_float32Array[i * 9 + 4] =
|
|
192
|
-
_float32Array[i * 9 + 5] =
|
|
190
|
+
_float32Array[i * 9 + 3] = coordsInner[0];
|
|
191
|
+
_float32Array[i * 9 + 4] = coordsInner[1];
|
|
192
|
+
_float32Array[i * 9 + 5] = coordsInner[2];
|
|
193
193
|
}
|
|
194
194
|
return _float32Array.subarray(0, 360 * 3 * 3);
|
|
195
195
|
}
|
|
@@ -215,11 +215,11 @@ export class Padding1DegreePlugin {
|
|
|
215
215
|
this._float32Array[i * 12] = color[0];
|
|
216
216
|
this._float32Array[i * 12 + 1] = color[1];
|
|
217
217
|
this._float32Array[i * 12 + 2] = color[2];
|
|
218
|
-
this._float32Array[i * 12 + 3] = color[3]
|
|
218
|
+
this._float32Array[i * 12 + 3] = color[3];
|
|
219
219
|
this._float32Array[i * 12 + 4] = color[0];
|
|
220
220
|
this._float32Array[i * 12 + 5] = color[1];
|
|
221
221
|
this._float32Array[i * 12 + 6] = color[2];
|
|
222
|
-
this._float32Array[i * 12 + 7] = 0; //color[3]
|
|
222
|
+
this._float32Array[i * 12 + 7] = 0; //color[3] ;
|
|
223
223
|
}
|
|
224
224
|
return this._float32Array.subarray(0, 360 * 4 * 3);
|
|
225
225
|
}
|
|
@@ -233,11 +233,11 @@ export class Padding1DegreePlugin {
|
|
|
233
233
|
this._float32Array[i * 12] = color[0];
|
|
234
234
|
this._float32Array[i * 12 + 1] = color[1];
|
|
235
235
|
this._float32Array[i * 12 + 2] = color[2];
|
|
236
|
-
this._float32Array[i * 12 + 3] = color[3]
|
|
236
|
+
this._float32Array[i * 12 + 3] = color[3];
|
|
237
237
|
this._float32Array[i * 12 + 4] = color[0];
|
|
238
238
|
this._float32Array[i * 12 + 5] = color[1];
|
|
239
239
|
this._float32Array[i * 12 + 6] = color[2];
|
|
240
|
-
this._float32Array[i * 12 + 7] = 0; // color[3]
|
|
240
|
+
this._float32Array[i * 12 + 7] = 0; // color[3] ;
|
|
241
241
|
i++;
|
|
242
242
|
}
|
|
243
243
|
}
|
|
@@ -272,6 +272,9 @@ export class Padding1DegreePlugin {
|
|
|
272
272
|
console.warn("Globe or LineProgram or VAO is not initialized, cannot draw");
|
|
273
273
|
return;
|
|
274
274
|
}
|
|
275
|
+
if (this.globe.api_IsScreenMoving()) {
|
|
276
|
+
this.__build();
|
|
277
|
+
}
|
|
275
278
|
const gl = this.globe.gl;
|
|
276
279
|
const drawOptions = {
|
|
277
280
|
drawRange: {
|
|
@@ -280,7 +283,7 @@ export class Padding1DegreePlugin {
|
|
|
280
283
|
},
|
|
281
284
|
};
|
|
282
285
|
gl.disable(gl.DEPTH_TEST);
|
|
283
|
-
this.lineProgram.draw(this._vao, drawOptions, this.
|
|
286
|
+
this.lineProgram.draw(this._vao, drawOptions, this._userOpacity * this._adaptiveOpacityMultiplier, this._uboHandler);
|
|
284
287
|
gl.enable(gl.DEPTH_TEST);
|
|
285
288
|
}
|
|
286
289
|
free() {
|
|
@@ -310,17 +313,64 @@ export class Padding1DegreePlugin {
|
|
|
310
313
|
this._innerPaddingRatio = 1 - Math.pow(0.7, lod);
|
|
311
314
|
}
|
|
312
315
|
_buildPaddings(level) {
|
|
316
|
+
if (level === "input") {
|
|
317
|
+
// Build input paddings
|
|
318
|
+
}
|
|
319
|
+
else if (level === "innerCircle") {
|
|
320
|
+
// Build inner circle paddings
|
|
321
|
+
this.__innerCircle();
|
|
322
|
+
this.__elevation();
|
|
323
|
+
}
|
|
324
|
+
else if (level === "elevation") {
|
|
325
|
+
// Build elevation paddings
|
|
326
|
+
this.__elevation();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
__innerCircle() {
|
|
330
|
+
// Implement inner circle padding logic
|
|
331
|
+
console.log("innerCircle Level Update");
|
|
332
|
+
const datas = this._dataMap;
|
|
333
|
+
for (const [key, value] of datas) {
|
|
334
|
+
// Implement inner circle padding logic using key and value
|
|
335
|
+
const { innerCoords } = preAdapter(this.globe, value, this._innerPaddingRatio, value.outerCoords);
|
|
336
|
+
value.innerCoords = innerCoords;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
__elevation() {
|
|
340
|
+
this._updateAdaptiveOpacityMultiplier();
|
|
341
|
+
const bufferToUpdate = this.globe?.api_GetCurrentGeometry() === 1 ? "position2d" : "position3d";
|
|
342
|
+
console.log(bufferToUpdate);
|
|
343
|
+
const datas = this._dataMap;
|
|
344
|
+
const wrapper = [null];
|
|
345
|
+
this._float32Array = new Float32Array(360 * 4 * 4); // largest float32Array
|
|
346
|
+
for (const [key, value] of datas) {
|
|
347
|
+
wrapper[0] = value;
|
|
348
|
+
this.bufferOrchestrator.updateBulk(wrapper, this.bufferManagersMap, [bufferToUpdate]);
|
|
349
|
+
}
|
|
350
|
+
this._float32Array = null; // reset the float32Array
|
|
351
|
+
}
|
|
352
|
+
__build() {
|
|
353
|
+
const globeChanges = this.lineProgram?.cameraBlockTotem.getGlobeChanges();
|
|
354
|
+
if (globeChanges.lodChanged) {
|
|
355
|
+
console.log("lod changed");
|
|
356
|
+
}
|
|
357
|
+
if (globeChanges.lodChanged) {
|
|
358
|
+
this.__updateLODRelatedParameters();
|
|
359
|
+
this._buildPaddings("innerCircle");
|
|
360
|
+
}
|
|
361
|
+
else if (globeChanges.lookChanged || globeChanges.geometryChanged) {
|
|
362
|
+
this._buildPaddings("elevation");
|
|
363
|
+
}
|
|
313
364
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
// if cam change, recalculate 3d positions
|
|
365
|
+
_updateAdaptiveOpacityMultiplier() {
|
|
366
|
+
const currentLod = this.globe?.api_GetCurrentLODWithDecimal();
|
|
367
|
+
this._adaptiveOpacityMultiplier = Math.max(1 - (2.9 / currentLod), 0.1);
|
|
318
368
|
}
|
|
319
369
|
}
|
|
320
|
-
function preAdapter(globe, paddingInput, paddingRatio,
|
|
321
|
-
const
|
|
322
|
-
if (
|
|
323
|
-
|
|
370
|
+
function preAdapter(globe, paddingInput, paddingRatio, outerCoords) {
|
|
371
|
+
const innerCoords = new Array(360);
|
|
372
|
+
if (outerCoords === null) {
|
|
373
|
+
outerCoords = new Array(360);
|
|
324
374
|
for (let i = 0; i < 360; i++) {
|
|
325
375
|
const { long, lat } = globe.Math.FindPointByPolar(paddingInput.center[0], // center long
|
|
326
376
|
paddingInput.center[1], // center lat
|
|
@@ -333,25 +383,25 @@ function preAdapter(globe, paddingInput, paddingRatio, startCoords) {
|
|
|
333
383
|
const longDifference = Math.abs(long - endLong);
|
|
334
384
|
const latDifference = Math.abs(lat - endLat);
|
|
335
385
|
if (longDifference > 45) {
|
|
336
|
-
|
|
337
|
-
|
|
386
|
+
outerCoords[i] = null;
|
|
387
|
+
innerCoords[i] = null;
|
|
338
388
|
continue;
|
|
339
389
|
}
|
|
340
390
|
// Assign the calculated coordinates
|
|
341
|
-
|
|
342
|
-
|
|
391
|
+
outerCoords[i] = [long, lat];
|
|
392
|
+
innerCoords[i] = [endLong, endLat];
|
|
343
393
|
}
|
|
344
394
|
}
|
|
345
395
|
else {
|
|
346
|
-
// Handle case when
|
|
396
|
+
// Handle case when outerCoords is provided
|
|
347
397
|
for (let i = 0; i < 360; i++) {
|
|
348
|
-
if (
|
|
349
|
-
|
|
398
|
+
if (outerCoords[i] === null) {
|
|
399
|
+
innerCoords[i] = null;
|
|
350
400
|
continue;
|
|
351
401
|
}
|
|
352
402
|
const { long: endLong, lat: endLat } = globe.Math.FindPointByPolar(paddingInput.center[0], paddingInput.center[1], paddingInput.outerRadius * paddingRatio, i);
|
|
353
|
-
|
|
403
|
+
innerCoords[i] = [endLong, endLat];
|
|
354
404
|
}
|
|
355
405
|
}
|
|
356
|
-
return {
|
|
406
|
+
return { outerCoords, innerCoords };
|
|
357
407
|
}
|
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
// TODO: Delete this file if it is not needed anymore.
|
|
2
|
-
// import { normilze as vec3Normalize, dot as vec3Dot, cross as vec3Cross, scale as vec3Scale, add as vec3Add, sub as vec3Sub } from './vec3';
|
|
3
|
-
// --- Utility Functions for 3D Vector Operations ---
|
|
4
|
-
/**
|
|
5
|
-
* Normalizes a 3D vector to unit length.
|
|
6
|
-
* @param v The input vector.
|
|
7
|
-
* @returns The normalized vector. Returns [0,0,0] if the input vector is a zero vector.
|
|
8
|
-
*/
|
|
9
|
-
function vec3Normalize(v) {
|
|
10
|
-
const len = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
11
|
-
if (len < 1e-9) { // Use a small epsilon to handle near-zero vectors
|
|
12
|
-
return [0, 0, 0];
|
|
13
|
-
}
|
|
14
|
-
return [v[0] / len, v[1] / len, v[2] / len];
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Calculates the dot product of two 3D vectors.
|
|
18
|
-
* @param v1 The first vector.
|
|
19
|
-
* @param v2 The second vector.
|
|
20
|
-
* @returns The dot product.
|
|
21
|
-
*/
|
|
22
|
-
function vec3Dot(v1, v2) {
|
|
23
|
-
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Calculates the cross product of two 3D vectors.
|
|
27
|
-
* @param v1 The first vector.
|
|
28
|
-
* @param v2 The second vector.
|
|
29
|
-
* @returns The cross product vector.
|
|
30
|
-
*/
|
|
31
|
-
function vec3Cross(v1, v2) {
|
|
32
|
-
return [
|
|
33
|
-
v1[1] * v2[2] - v1[2] * v2[1],
|
|
34
|
-
v1[2] * v2[0] - v1[0] * v2[2],
|
|
35
|
-
v1[0] * v2[1] - v1[1] * v2[0]
|
|
36
|
-
];
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Scales a 3D vector by a scalar.
|
|
40
|
-
* @param v The input vector.
|
|
41
|
-
* @param s The scalar value.
|
|
42
|
-
* @returns The scaled vector.
|
|
43
|
-
*/
|
|
44
|
-
function vec3Scale(v, s) {
|
|
45
|
-
return [v[0] * s, v[1] * s, v[2] * s];
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Adds two 3D vectors.
|
|
49
|
-
* @param v1 The first vector.
|
|
50
|
-
* @param v2 The second vector.
|
|
51
|
-
* @returns The resulting vector.
|
|
52
|
-
*/
|
|
53
|
-
function vec3Add(v1, v2) {
|
|
54
|
-
return [v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2]];
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Subtracts the second 3D vector from the first.
|
|
58
|
-
* @param v1 The first vector.
|
|
59
|
-
* @param v2 The second vector.
|
|
60
|
-
* @returns The resulting vector.
|
|
61
|
-
*/
|
|
62
|
-
function vec3Sub(v1, v2) {
|
|
63
|
-
return [v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2]];
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Clamps a numerical value within a specified range.
|
|
67
|
-
* @param value The value to clamp.
|
|
68
|
-
* @param min The minimum allowed value.
|
|
69
|
-
* @param max The maximum allowed value.
|
|
70
|
-
* @returns The clamped value.
|
|
71
|
-
*/
|
|
72
|
-
function clamp(value, min, max) {
|
|
73
|
-
return Math.max(min, Math.min(value, max));
|
|
74
|
-
}
|
|
75
|
-
function vec3Distance(v1, v2) {
|
|
76
|
-
const dx = v1[0] - v2[0];
|
|
77
|
-
const dy = v1[1] - v2[1];
|
|
78
|
-
const dz = v1[2] - v2[2];
|
|
79
|
-
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Rotates a vector around a given axis by a specified angle using Rodrigues' rotation formula.
|
|
83
|
-
* The axis must be a unit vector for correct results.
|
|
84
|
-
* @param v The vector to rotate.
|
|
85
|
-
* @param axis The unit axis of rotation.
|
|
86
|
-
* @param angle The angle of rotation in radians.
|
|
87
|
-
* @returns The rotated vector.
|
|
88
|
-
*/
|
|
89
|
-
function rotateVector(v, axis, angle) {
|
|
90
|
-
const cosAngle = Math.cos(angle);
|
|
91
|
-
const sinAngle = Math.sin(angle);
|
|
92
|
-
const oneMinusCos = 1.0 - cosAngle;
|
|
93
|
-
const dotAxisV = vec3Dot(axis, v);
|
|
94
|
-
const crossAxisV = vec3Cross(axis, v);
|
|
95
|
-
// Rodrigues' rotation formula:
|
|
96
|
-
// v' = v * cos(angle) + (axis x v) * sin(angle) + axis * (axis . v) * (1 - cos(angle))
|
|
97
|
-
const term1 = vec3Scale(v, cosAngle);
|
|
98
|
-
const term2 = vec3Scale(crossAxisV, sinAngle);
|
|
99
|
-
const term3 = vec3Scale(axis, dotAxisV * oneMinusCos);
|
|
100
|
-
return vec3Add(vec3Add(term1, term2), term3);
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Solves a quadratic equation Ax^2 + Bx + C = 0 for x, returning a root within a specified range.
|
|
104
|
-
* Handles linear cases (A approx 0) and multiple roots, selecting the most appropriate one for CDF inversion.
|
|
105
|
-
* @param a Coefficient A.
|
|
106
|
-
* @param b Coefficient B.
|
|
107
|
-
* @param c Coefficient C.
|
|
108
|
-
* @param minVal The minimum expected value for the root.
|
|
109
|
-
* @param maxVal The maximum expected value for the root.
|
|
110
|
-
* @returns The selected root within the range, or null if no valid real root.
|
|
111
|
-
*/
|
|
112
|
-
function solveQuadratic(a, b, c, minVal, maxVal) {
|
|
113
|
-
const epsilon = 1e-12; // Small value for floating point comparisons
|
|
114
|
-
// Case 1: Linear equation (A is close to zero)
|
|
115
|
-
if (Math.abs(a) < epsilon) {
|
|
116
|
-
if (Math.abs(b) < epsilon) {
|
|
117
|
-
// Both A and B are zero. If C is also zero, infinitely many solutions, pick minVal.
|
|
118
|
-
// Otherwise, no solutions.
|
|
119
|
-
return Math.abs(c) < epsilon ? minVal : null;
|
|
120
|
-
}
|
|
121
|
-
const x = -c / b;
|
|
122
|
-
// Check if the linear solution is within the expected range
|
|
123
|
-
return (x >= minVal - epsilon && x <= maxVal + epsilon) ? x : null;
|
|
124
|
-
}
|
|
125
|
-
// Case 2: Quadratic equation
|
|
126
|
-
const discriminant = b * b - 4 * a * c;
|
|
127
|
-
if (discriminant < -epsilon) { // No real roots (discriminant is significantly negative)
|
|
128
|
-
console.log("No real roots for the quadratic equation.");
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
if (discriminant < epsilon) { // One real root (discriminant is close to zero)
|
|
132
|
-
const x = -b / (2 * a);
|
|
133
|
-
return (x >= minVal - epsilon && x <= maxVal + epsilon) ? x : null;
|
|
134
|
-
}
|
|
135
|
-
// Case 3: Two distinct real roots
|
|
136
|
-
const sqrtDisc = Math.sqrt(discriminant);
|
|
137
|
-
const x1 = (-b + sqrtDisc) / (2 * a);
|
|
138
|
-
const x2 = (-b - sqrtDisc) / (2 * a);
|
|
139
|
-
const validX1 = (x1 >= minVal - epsilon && x1 <= maxVal + epsilon);
|
|
140
|
-
const validX2 = (x2 >= minVal - epsilon && x2 <= maxVal + epsilon);
|
|
141
|
-
if (validX1 && validX2) {
|
|
142
|
-
// If both roots are valid, pick the one that is logically correct for CDF inversion.
|
|
143
|
-
// For an increasing CDF, we generally want the smallest valid non-negative root.
|
|
144
|
-
return Math.min(x1, x2);
|
|
145
|
-
}
|
|
146
|
-
else if (validX1) {
|
|
147
|
-
return x1;
|
|
148
|
-
}
|
|
149
|
-
else if (validX2) {
|
|
150
|
-
return x2;
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
return null; // No valid roots within the range
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
// --- Main Function for Arc Point Generation ---
|
|
157
|
-
/**
|
|
158
|
-
* Generates points on the shortest arc between p0 and p1 on a unit sphere.
|
|
159
|
-
* The arc is formed by rotating p0 around axisA. The density of points
|
|
160
|
-
* is higher closer to the attractionPoint.
|
|
161
|
-
*
|
|
162
|
-
* @param p0 The starting point of the arc (will be normalized).
|
|
163
|
-
* @param p1 The ending point of the arc (will be normalized).
|
|
164
|
-
* @param axisA The axis of rotation (will be normalized).
|
|
165
|
-
* @param attractionPoint The point on the unit sphere that attracts sampled points (will be normalized).
|
|
166
|
-
* @param pointCount The total number of points to generate on the arc, including p0 and p1.
|
|
167
|
-
* @param strength Controls the density bias. A value of 0 means uniform sampling.
|
|
168
|
-
* Higher positive values (e.g., 1 to 10) result in stronger attraction.
|
|
169
|
-
* @returns An array of `Vec3` representing the deterministically sampled points on the arc.
|
|
170
|
-
*/
|
|
171
|
-
export function generateArcPoints(p0, p1, axisA, attractionPoint, pointCount, strength) {
|
|
172
|
-
const sampledPoints = [];
|
|
173
|
-
const epsilon = 1e-7; // General epsilon for vector comparisons
|
|
174
|
-
// 1. Normalize all input vectors
|
|
175
|
-
let nP0 = vec3Normalize(p0);
|
|
176
|
-
let nP1 = vec3Normalize(p1);
|
|
177
|
-
let nAxisA = vec3Normalize(axisA);
|
|
178
|
-
let nAttractionPoint = vec3Normalize(attractionPoint);
|
|
179
|
-
// Handle edge cases for pointCount
|
|
180
|
-
if (pointCount < 1) {
|
|
181
|
-
return sampledPoints;
|
|
182
|
-
}
|
|
183
|
-
if (pointCount === 1) {
|
|
184
|
-
sampledPoints.push(nP0);
|
|
185
|
-
return sampledPoints;
|
|
186
|
-
}
|
|
187
|
-
// 2. Determine the total rotation angle required to go from p0 to p1 around axisA
|
|
188
|
-
// Project p0 and p1 onto the plane perpendicular to axisA
|
|
189
|
-
const p0Projected = vec3Sub(nP0, vec3Scale(nAxisA, vec3Dot(nP0, nAxisA)));
|
|
190
|
-
const p1Projected = vec3Sub(nP1, vec3Scale(nAxisA, vec3Dot(nP1, nAxisA)));
|
|
191
|
-
const lenP0Proj = Math.sqrt(vec3Dot(p0Projected, p0Projected));
|
|
192
|
-
const lenP1Proj = Math.sqrt(vec3Dot(p1Projected, p1Projected));
|
|
193
|
-
// Handle cases where p0 or p1 are colinear with the rotation axis
|
|
194
|
-
if (lenP0Proj < epsilon || lenP1Proj < epsilon) {
|
|
195
|
-
// If start and end points are effectively the same (on or very near the axis)
|
|
196
|
-
if (Math.abs(vec3Dot(nP0, nP1) - 1.0) < epsilon) {
|
|
197
|
-
for (let i = 0; i < pointCount; i++) {
|
|
198
|
-
sampledPoints.push(nP0); // Return identical points as no meaningful arc exists
|
|
199
|
-
}
|
|
200
|
-
return sampledPoints;
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
console.warn("generateArcPoints: p0 or p1 is colinear with axisA. Cannot form a valid arc by rotation around axisA to reach p1. Falling back to linear interpolation.");
|
|
204
|
-
// Fallback: simple linear interpolation and normalize
|
|
205
|
-
for (let i = 0; i < pointCount; i++) {
|
|
206
|
-
const t = i / (pointCount - 1);
|
|
207
|
-
const interpolated = vec3Add(vec3Scale(nP0, (1 - t)), vec3Scale(nP1, t));
|
|
208
|
-
sampledPoints.push(vec3Normalize(interpolated));
|
|
209
|
-
}
|
|
210
|
-
return sampledPoints;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
const nP0Projected = vec3Normalize(p0Projected);
|
|
214
|
-
const nP1Projected = vec3Normalize(p1Projected);
|
|
215
|
-
// Calculate total rotation angle using atan2 for a signed angle
|
|
216
|
-
const crossProj = vec3Cross(nP0Projected, nP1Projected);
|
|
217
|
-
const totalRotationAngle = Math.atan2(vec3Dot(crossProj, nAxisA), vec3Dot(nP0Projected, nP1Projected));
|
|
218
|
-
// If total rotation angle is negligible, all points are essentially p0
|
|
219
|
-
if (Math.abs(totalRotationAngle) < epsilon) {
|
|
220
|
-
for (let i = 0; i < pointCount; i++) {
|
|
221
|
-
sampledPoints.push(nP0);
|
|
222
|
-
}
|
|
223
|
-
return sampledPoints;
|
|
224
|
-
}
|
|
225
|
-
// 3. Find alpha_A: the angle parameter (from p0) where the arc point is closest to attractionPoint
|
|
226
|
-
// We search over `t` from 0 to 1, and map to actual rotation angles.
|
|
227
|
-
let alpha_A = 0.0; // This will be the actual rotation angle
|
|
228
|
-
let minAngleToAttraction = Math.PI; // Initialize with max possible angle
|
|
229
|
-
// TODO: USE GEOMETRY INSTEAD OF MARKOV CHOICE
|
|
230
|
-
const testSteps = 1000; // Granularity for finding alpha_A
|
|
231
|
-
for (let i = 0; i <= testSteps; i++) {
|
|
232
|
-
const t_test = i / testSteps; // Normalized parameter [0, 1]
|
|
233
|
-
const currentAlpha = totalRotationAngle * t_test; // Actual rotation angle
|
|
234
|
-
const pArcTest = rotateVector(nP0, nAxisA, currentAlpha); // Point on arc
|
|
235
|
-
const dotProduct = clamp(vec3Dot(pArcTest, nAttractionPoint), -1.0, 1.0);
|
|
236
|
-
const currentAngle = Math.acos(dotProduct);
|
|
237
|
-
if (currentAngle < minAngleToAttraction) {
|
|
238
|
-
minAngleToAttraction = currentAngle;
|
|
239
|
-
alpha_A = currentAlpha;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
// minAngleToAttraction = 0.0;
|
|
243
|
-
console.log("generated alpha_A:", alpha_A, "minAngleToAttraction:", minAngleToAttraction);
|
|
244
|
-
// 4. Define the PDF parameters (for normalized parameter t in [0,1])
|
|
245
|
-
// The density function for 't' (normalized angle from 0 to 1)
|
|
246
|
-
const minDensity = 1.0;
|
|
247
|
-
// Strength ensures positive density. Higher strength gives more pronounced peak.
|
|
248
|
-
const maxDensity = minDensity + Math.max(0, strength);
|
|
249
|
-
// Normalize alpha_A to the [0, 1] range for the PDF/CDF calculations
|
|
250
|
-
// This represents the peak position (t_peak) within the normalized parameter space.
|
|
251
|
-
const alpha_A_normalized = totalRotationAngle !== 0 ? clamp(alpha_A / totalRotationAngle, 0, 1) : 0;
|
|
252
|
-
// Normalization constant for the PDF (integral over [0,1] must be 1)
|
|
253
|
-
// Area of trapezoid = 0.5 * (base1 + base2) * height
|
|
254
|
-
// In our case, the "bases" are minDensity and maxDensity, "height" is 1 (the t-interval length)
|
|
255
|
-
const pdfArea = 0.5 * (minDensity + maxDensity);
|
|
256
|
-
const pdfNormalizationConstant = 1.0 / pdfArea;
|
|
257
|
-
// Calculate CDF value at the peak point (alpha_A_normalized)
|
|
258
|
-
// This splits the CDF into two regions for the quadratic solver
|
|
259
|
-
const cdfAtAlphaA_norm = pdfNormalizationConstant * (minDensity * alpha_A_normalized + 0.5 * (maxDensity - minDensity) * alpha_A_normalized);
|
|
260
|
-
// 5. Generate deterministic points using inverse transform sampling
|
|
261
|
-
for (let i = 0; i < pointCount; i++) {
|
|
262
|
-
// Generate uniformly spaced values `u` in [0, 1]
|
|
263
|
-
const u = i / (pointCount - 1);
|
|
264
|
-
let t_sampled; // This will be the sampled normalized parameter [0, 1]
|
|
265
|
-
if (u <= cdfAtAlphaA_norm) {
|
|
266
|
-
// Case 1: The sampled point falls in the first part of the PDF (0 to alpha_A_normalized)
|
|
267
|
-
// F(t) = normConst * (minDensity * t + 0.5 * (maxDensity - minDensity) / alpha_A_normalized * t^2)
|
|
268
|
-
// Rearrange to A*t^2 + B*t + C = 0 for solving:
|
|
269
|
-
const A = (alpha_A_normalized !== 0) ? pdfNormalizationConstant * 0.5 * (maxDensity - minDensity) / alpha_A_normalized : 0;
|
|
270
|
-
const B = pdfNormalizationConstant * minDensity;
|
|
271
|
-
const C = -u;
|
|
272
|
-
const result = solveQuadratic(A, B, C, 0, alpha_A_normalized);
|
|
273
|
-
// Fallback for unexpected null (e.g., numerical issues)
|
|
274
|
-
t_sampled = result !== null ? result : (u / (cdfAtAlphaA_norm || epsilon)) * alpha_A_normalized;
|
|
275
|
-
}
|
|
276
|
-
else {
|
|
277
|
-
// Case 2: The sampled point falls in the second part of the PDF (alpha_A_normalized to 1)
|
|
278
|
-
// F(t) - cdfAtAlphaA_norm = normConst * [ maxDensity * (t - alpha_A_normalized) - 0.5 * (maxDensity - minDensity) / (1 - alpha_A_normalized) * (t - alpha_A_normalized)^2 ]
|
|
279
|
-
// Let x = t - alpha_A_normalized. Solve for x: A'*x^2 + B'*x + C' = 0
|
|
280
|
-
const rangeAfterA = 1 - alpha_A_normalized;
|
|
281
|
-
const A_prime = (rangeAfterA !== 0) ? -pdfNormalizationConstant * 0.5 * (maxDensity - minDensity) / rangeAfterA : 0;
|
|
282
|
-
const B_prime = pdfNormalizationConstant * maxDensity;
|
|
283
|
-
const C_prime = -(u - cdfAtAlphaA_norm);
|
|
284
|
-
const result_x = solveQuadratic(A_prime, B_prime, C_prime, 0, rangeAfterA);
|
|
285
|
-
// Fallback for unexpected null
|
|
286
|
-
t_sampled = (result_x !== null ? alpha_A_normalized + result_x :
|
|
287
|
-
alpha_A_normalized + (u - cdfAtAlphaA_norm) / ((1 - cdfAtAlphaA_norm) || epsilon) * rangeAfterA);
|
|
288
|
-
}
|
|
289
|
-
// Ensure t_sampled is within [0, 1] due to potential float inaccuracies
|
|
290
|
-
t_sampled = clamp(t_sampled, 0, 1);
|
|
291
|
-
// Convert the normalized parameter 't_sampled' back to the actual rotation angle
|
|
292
|
-
const actualRotationAngle = totalRotationAngle * t_sampled;
|
|
293
|
-
// Rotate the starting point p0 by this angle around axisA to get the final point
|
|
294
|
-
const currentPoint = rotateVector(nP0, nAxisA, actualRotationAngle);
|
|
295
|
-
sampledPoints.push(currentPoint);
|
|
296
|
-
}
|
|
297
|
-
return sampledPoints;
|
|
298
|
-
}
|
|
299
|
-
// --- Example Usage (for demonstration and testing) ---
|
|
300
|
-
// Example 1: Basic arc, attraction point in the middle
|
|
301
|
-
const p0_ex1 = [1, 0, 0];
|
|
302
|
-
const p1_ex1 = [0, 1, 0]; // 90 deg rotation around Z-axis
|
|
303
|
-
const axisA_ex1 = [0, 0, 1];
|
|
304
|
-
const attractionPoint_ex1 = [Math.sqrt(0.5), Math.sqrt(0.5), 0]; // On the arc, middle of P0-P1
|
|
305
|
-
const numPoints_ex1 = 50;
|
|
306
|
-
const strength_ex1 = 100.0; // Strong attraction
|
|
307
|
-
console.log("--- Example 1: Attraction point in the middle of the arc ---");
|
|
308
|
-
const points1 = generateArcPoints(p0_ex1, p1_ex1, axisA_ex1, attractionPoint_ex1, numPoints_ex1, strength_ex1);
|
|
309
|
-
console.log(`Generated ${points1.length} points.`);
|
|
310
|
-
// To observe concentration, you might visualize these points or calculate their 't' parameter distribution.
|
|
311
|
-
// For console output, let's print a few:
|
|
312
|
-
console.log("First 5 points:", points1.slice(0, 5).map(p => p.map(coord => coord.toFixed(4))));
|
|
313
|
-
console.log("Last 5 points:", points1.slice(-5).map(p => p.map(coord => coord.toFixed(4))));
|
|
314
|
-
// Expected: Points should be denser around [0.707, 0.707, 0]
|
|
315
|
-
const sequentialDistances1 = points1.map((p, i) => {
|
|
316
|
-
if (i === 0)
|
|
317
|
-
return 0; // Skip the first point
|
|
318
|
-
return vec3Distance(points1[i - 1], p);
|
|
319
|
-
});
|
|
320
|
-
console.log("Sequential distances between points (should be roughly equal for uniform distribution):", sequentialDistances1.map(d => d.toFixed(4)));
|
|
321
|
-
// Example 2: Attraction point near p0
|
|
322
|
-
const p0_ex2 = [1, 0, 0];
|
|
323
|
-
const p1_ex2 = [0, 1, 0];
|
|
324
|
-
const axisA_ex2 = [0, 0, 1];
|
|
325
|
-
const attractionPoint_ex2 = [0.99, 0.01, 0]; // Very close to P0
|
|
326
|
-
const numPoints_ex2 = 50;
|
|
327
|
-
const strength_ex2 = 10.0; // Very strong attraction
|
|
328
|
-
console.log("\n--- Example 2: Attraction point near the start (p0) ---");
|
|
329
|
-
const points2 = generateArcPoints(p0_ex2, p1_ex2, axisA_ex2, attractionPoint_ex2, numPoints_ex2, strength_ex2);
|
|
330
|
-
console.log(`Generated ${points2.length} points.`);
|
|
331
|
-
console.log("First 5 points:", points2.slice(0, 5).map(p => p.map(coord => coord.toFixed(4))));
|
|
332
|
-
console.log("Last 5 points:", points2.slice(-5).map(p => p.map(coord => coord.toFixed(4))));
|
|
333
|
-
// Expected: Points should be denser near [1, 0, 0]
|
|
334
|
-
// Example 3: Attraction point away from the arc (expect less concentration)
|
|
335
|
-
const p0_ex3 = [1, 0, 0];
|
|
336
|
-
const p1_ex3 = [0, 1, 0];
|
|
337
|
-
const axisA_ex3 = [0, 0, 1];
|
|
338
|
-
const attractionPoint_ex3 = [0, 0, 1]; // North pole, away from the XY plane arc
|
|
339
|
-
const numPoints_ex3 = 50;
|
|
340
|
-
const strength_ex3 = 5.0;
|
|
341
|
-
console.log("\n--- Example 3: Attraction point away from the arc ---");
|
|
342
|
-
const points3 = generateArcPoints(p0_ex3, p1_ex3, axisA_ex3, attractionPoint_ex3, numPoints_ex3, strength_ex3);
|
|
343
|
-
console.log(`Generated ${points3.length} points.`);
|
|
344
|
-
console.log("First 5 points:", points3.slice(0, 5).map(p => p.map(coord => coord.toFixed(4))));
|
|
345
|
-
console.log("Last 5 points:", points3.slice(-5).map(p => p.map(coord => coord.toFixed(4))));
|
|
346
|
-
// Expected: Points should be relatively uniformly distributed, as no point on the arc is significantly closer.
|
|
347
|
-
// The "closest point" on the arc will likely be determined by its closest angular projection.
|
|
348
|
-
// Example 4: No attraction (uniform distribution)
|
|
349
|
-
const p0_ex4 = [1, 0, 0];
|
|
350
|
-
const p1_ex4 = [0, 1, 0];
|
|
351
|
-
const axisA_ex4 = [0, 0, 1];
|
|
352
|
-
const attractionPoint_ex4 = [Math.sqrt(1 / 2), Math.sqrt(1 / 2), 0]; // Irrelevant when strength is 0
|
|
353
|
-
const numPoints_ex4 = 50;
|
|
354
|
-
const strength_ex4 = 4.0; // No attraction (uniform)
|
|
355
|
-
console.log("\n--- Example 4: Uniform distribution (strength = 0) ---");
|
|
356
|
-
const points4 = generateArcPoints(p0_ex4, p1_ex4, axisA_ex4, attractionPoint_ex4, numPoints_ex4, strength_ex4);
|
|
357
|
-
console.log(`Generated ${points4.length} points.`);
|
|
358
|
-
console.log("First 5 points:", points4.slice(0, 5).map(p => p.map(coord => coord.toFixed(4))));
|
|
359
|
-
console.log("Last 5 points:", points4.slice(-5).map(p => p.map(coord => coord.toFixed(4))));
|
|
360
|
-
// Expected: Points should be uniformly spaced.
|
|
361
|
-
const sequentialDistances = points4.map((p, i) => {
|
|
362
|
-
if (i === 0)
|
|
363
|
-
return 0; // Skip the first point
|
|
364
|
-
return vec3Distance(points4[i - 1], p);
|
|
365
|
-
});
|
|
366
|
-
console.log("Sequential distances between points (should be roughly equal for uniform distribution):", sequentialDistances.map(d => d.toFixed(4)));
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// TODO: REMOVE
|
|
3
|
-
// // import { Vector3D, Quaternion, Plane, Ray, Matrix4 } from 'your-3d-math-library'; // <-- ADAPT THIS IMPORT
|
|
4
|
-
// import { Vector3D } from '../vector3d';
|
|
5
|
-
// import { Plane } from '../plane';
|
|
6
|
-
// import { Line } from '../line';
|
|
7
|
-
// import { Matrix4 } from '../matrix4';
|
|
8
|
-
// import { Quaternion } from '../quaternion';
|
|
9
|
-
// // --- Assume these static helper variables exist within your class ---
|
|
10
|
-
// const _lookFromOrigin = new Vector3D();
|
|
11
|
-
// const _cameraPosition = new Vector3D();
|
|
12
|
-
// const _finalForward = new Vector3D();
|
|
13
|
-
// const _approxUp = new Vector3D();
|
|
14
|
-
// const _finalRight = new Vector3D();
|
|
15
|
-
// const _finalUp = new Vector3D();
|
|
16
|
-
// const _tempVec = new Vector3D(); // General purpose helper
|
|
17
|
-
// const _initialBottomRayDir = new Vector3D();
|
|
18
|
-
// const _qOrient = new Quaternion();
|
|
19
|
-
// const _rollQuaternion = new Quaternion();
|
|
20
|
-
// const _rotationMatrix = new Matrix4();
|
|
21
|
-
// const _fieldOfViewBottomRay = new Line(new Vector3D(), new Vector3D());
|
|
22
|
-
// const _origin3d = new Vector3D(0, 0, 0); // Globe Center
|
|
23
|
-
// // --- Assume these constants exist ---
|
|
24
|
-
// const WORLD_RADIUS_3D = 6371000; // Example: Earth radius
|
|
25
|
-
// const FIELD_OF_VIEW = (25 * Math.PI) / 180; // Example: 25 degrees half-FOV in radians
|
|
26
|
-
// // // --- Assume Plane class has these methods ---
|
|
27
|
-
// // interface Plane {
|
|
28
|
-
// // setFromNormalAndPoint(normal: Vector3D, point: Vector3D): this;
|
|
29
|
-
// // set(normal: Vector3D, constant: number): this;
|
|
30
|
-
// // // ... other plane methods
|
|
31
|
-
// // }
|
|
32
|
-
// // // --- Assume Ray class has these methods ---
|
|
33
|
-
// // interface Ray {
|
|
34
|
-
// // origin: Vector3D;
|
|
35
|
-
// // direction: Vector3D;
|
|
36
|
-
// // intersectionSphere(center: Vector3D, radius: number): Vector3D[] | null; // Or similar
|
|
37
|
-
// // // ... other ray methods
|
|
38
|
-
// // }
|
|
39
|
-
// // // --- Assume Vector3D has setFromLongLat ---
|
|
40
|
-
// // interface Vector3D {
|
|
41
|
-
// // setFromLongLat(lon: number, lat: number): this;
|
|
42
|
-
// // // ... other vector methods
|
|
43
|
-
// // }
|
|
44
|
-
// // // --- END OF ASSUMPTIONS ---
|
|
45
|
-
// /**
|
|
46
|
-
// * Calculates a culling plane representing the bottom edge of the camera's field of view.
|
|
47
|
-
// * @param globe - The globe object, expected to have api_GetCurrentLookInfo and Fp properties.
|
|
48
|
-
// * @param out - The Plane object to store the result.
|
|
49
|
-
// * @param fieldOfView - The vertical half-angle of the field of view in radians.
|
|
50
|
-
// * @returns The calculated Plane.
|
|
51
|
-
// */
|
|
52
|
-
// function calculateHorizonPlane(globe: any, out: Plane, fieldOfView: number = FIELD_OF_VIEW): Plane {
|
|
53
|
-
// const cameraLookInfo = globe.api_GetCurrentLookInfo();
|
|
54
|
-
// cameraLookInfo.CenterLong *= Math.PI / 180;
|
|
55
|
-
// cameraLookInfo.CenterLat *= Math.PI / 180;
|
|
56
|
-
// // cameraLookInfo.Tilt *= Math.PI / 180; // Tilt is now implicitly handled
|
|
57
|
-
// cameraLookInfo.NorthAng *= Math.PI / 180;
|
|
58
|
-
// // Set _lookFromOrigin based on camera target (Lon/Lat)
|
|
59
|
-
// (_lookFromOrigin as any).setFromLongLat(cameraLookInfo.CenterLong, cameraLookInfo.CenterLat); // Cast to any if setFromLongLat is custom
|
|
60
|
-
// // 1. Calculate _cameraPosition and _finalForward (_lookFromCamera)
|
|
61
|
-
// _cameraPosition.set(globe.Fp.x, globe.Fp.y, globe.Fp.z); // Using your X negation
|
|
62
|
-
// _cameraPosition.divideScalar(WORLD_RADIUS_3D); // Scale to unit sphere
|
|
63
|
-
// Vector3D.subVectors(_lookFromOrigin, _cameraPosition, _finalForward).normalize();
|
|
64
|
-
// // 2. Calculate Q_orient (Final Camera Orientation Quaternion)
|
|
65
|
-
// // Use the vector from origin to camera as an initial 'up' reference
|
|
66
|
-
// _approxUp.copy(_cameraPosition).normalize();
|
|
67
|
-
// // Calculate Right vector using cross product
|
|
68
|
-
// Vector3D.crossVectors(_finalForward, _approxUp, _finalRight).normalize();
|
|
69
|
-
// // Handle edge case: If looking straight along (or opposite to) _approxUp
|
|
70
|
-
// if (_finalRight.lengthSq() < 0.0001) {
|
|
71
|
-
// // If vectors are parallel, _approxUp is not good.
|
|
72
|
-
// // Choose a different 'up', e.g., if _approxUp is near Z, use Y.
|
|
73
|
-
// if (Math.abs(_approxUp.z) > 0.9) {
|
|
74
|
-
// _approxUp.set(0, 1, 0);
|
|
75
|
-
// } else {
|
|
76
|
-
// _approxUp.set(0, 0, 1);
|
|
77
|
-
// }
|
|
78
|
-
// Vector3D.crossVectors(_finalForward, _approxUp, _finalRight).normalize();
|
|
79
|
-
// }
|
|
80
|
-
// // Calculate the 'true' Up vector, orthogonal to Right and Forward
|
|
81
|
-
// Vector3D.crossVectors(_finalRight, _finalForward, _finalUp).normalize();
|
|
82
|
-
// // Apply NorthAng (Roll) around the Forward axis
|
|
83
|
-
// _rollQuaternion.setFromAxisAngle(_finalForward, cameraLookInfo.NorthAng);
|
|
84
|
-
// _finalUp.applyQuaternion(_rollQuaternion);
|
|
85
|
-
// _finalRight.applyQuaternion(_rollQuaternion); // Must rotate Right as well!
|
|
86
|
-
// // Create Q_orient from the final basis vectors (Right, Up, -Forward)
|
|
87
|
-
// // This assumes a Matrix4.makeBasis(x, y, z) exists and builds a rotation matrix.
|
|
88
|
-
// // It also assumes local camera looks down -Z, hence negating _finalForward for the Z basis.
|
|
89
|
-
// _tempVec.copy(_finalForward).negate();
|
|
90
|
-
// _rotationMatrix.makeBasis(_finalRight, _finalUp, _tempVec); // <-- ADAPT: Ensure your library has makeBasis or equivalent
|
|
91
|
-
// _qOrient.setFromRotationMatrix(_rotationMatrix); // <-- ADAPT: Ensure this function exists
|
|
92
|
-
// // 3. Calculate Initial Bottom Ray (Local: -Z forward, Y up, X right)
|
|
93
|
-
// // Rotates (0, 0, -1) around (1, 0, 0) by fieldOfView
|
|
94
|
-
// _initialBottomRayDir.set(0, -Math.sin(fieldOfView), -Math.cos(fieldOfView));
|
|
95
|
-
// // 4. Calculate Final Bottom Ray (World)
|
|
96
|
-
// _fieldOfViewBottomRay.direction.copy(_initialBottomRayDir).applyQuaternion(_qOrient).normalize();
|
|
97
|
-
// _fieldOfViewBottomRay.origin.copy(_cameraPosition);
|
|
98
|
-
// // 5. Intersection and Plane
|
|
99
|
-
// const intersection = (_fieldOfViewBottomRay as any).intersectionSphere(_origin3d, 1); // Cast to any if method isn't standard
|
|
100
|
-
// if (intersection && intersection.length > 0) {
|
|
101
|
-
// // If ray intersects, use the closest point for the plane
|
|
102
|
-
// out.setFromNormalAndPoint(_lookFromOrigin, intersection[0]);
|
|
103
|
-
// } else {
|
|
104
|
-
// // If ray does not intersect (FOV covers globe or misses)
|
|
105
|
-
// // WARNING: This sets a plane through the globe's center.
|
|
106
|
-
// // This might not be the desired behavior for culling.
|
|
107
|
-
// // Consider what should happen if the whole globe *is* visible.
|
|
108
|
-
// out.set(_lookFromOrigin, 0.0);
|
|
109
|
-
// }
|
|
110
|
-
// return out;
|
|
111
|
-
// }
|
|
112
|
-
// export { calculateHorizonPlane };
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import "./types";
|
|
2
|
-
/**
|
|
3
|
-
* @param {DrawSubsetOptions} drawSubsetOptions
|
|
4
|
-
*/
|
|
5
|
-
// TODO: Draw all is an optional property for the target plugin, with this approach.
|
|
6
|
-
class DrawSubsetOptionRegistry {
|
|
7
|
-
constructor() {
|
|
8
|
-
this._drawSubsetOptions = new Map();
|
|
9
|
-
}
|
|
10
|
-
register(key, drawSubsetOptions) {
|
|
11
|
-
this._drawSubsetOptions.set(key, drawSubsetOptions);
|
|
12
|
-
}
|
|
13
|
-
unregister(key) {
|
|
14
|
-
this._drawSubsetOptions.delete(key);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { ArcOnTerrainPlugin } from "../arc-plugin";
|
|
2
|
-
const EDGE_COUNT = 5;
|
|
3
|
-
const paddingKeys = (padding) => {
|
|
4
|
-
const stepCount = padding.coverAngle / padding.stepAngle;
|
|
5
|
-
const keys = new Array(Math.ceil(stepCount));
|
|
6
|
-
for (let i = 0; i < stepCount; i++) {
|
|
7
|
-
keys[i] = padding.key + i;
|
|
8
|
-
}
|
|
9
|
-
if (keys.length > stepCount) {
|
|
10
|
-
keys[keys.length - 1] = padding.key + stepCount;
|
|
11
|
-
}
|
|
12
|
-
return keys;
|
|
13
|
-
};
|
|
14
|
-
const adapterPadding2Arc = (globe, padding) => {
|
|
15
|
-
const stepCount = padding.coverAngle / padding.stepAngle;
|
|
16
|
-
const result = new Array(Math.ceil(stepCount));
|
|
17
|
-
const fill = (i, angle) => {
|
|
18
|
-
const startPoint = globe.Math.FindPointByPolar(padding.center[0], padding.center[1], padding.outerRadius, angle);
|
|
19
|
-
const endPoint = globe.Math.FindPointByPolar(padding.center[0], padding.center[1], padding.innerRadius, angle);
|
|
20
|
-
result[i] = {
|
|
21
|
-
key: padding.key + i,
|
|
22
|
-
start: [startPoint.long, startPoint.lat],
|
|
23
|
-
end: [endPoint.long, endPoint.lat],
|
|
24
|
-
color: padding.color,
|
|
25
|
-
height: padding.height,
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
for (let i = 0; i < stepCount; i++) {
|
|
29
|
-
const angle = padding.startAngle + i * padding.stepAngle;
|
|
30
|
-
fill(i, angle);
|
|
31
|
-
}
|
|
32
|
-
if (result.length > stepCount) {
|
|
33
|
-
const i = result.length - 1;
|
|
34
|
-
const angle = padding.startAngle + padding.coverAngle;
|
|
35
|
-
fill(i, angle);
|
|
36
|
-
}
|
|
37
|
-
return result;
|
|
38
|
-
};
|
|
39
|
-
export const chargerAdaptor = (chargerInput, plugin, padding) => {
|
|
40
|
-
};
|
|
41
|
-
export class PaddingPlugin {
|
|
42
|
-
id;
|
|
43
|
-
arcPlugin;
|
|
44
|
-
globe = null;
|
|
45
|
-
_memory = new Map();
|
|
46
|
-
isFreed = false;
|
|
47
|
-
constructor(id, { variativeColorsOn = true, defaultColor = [1, 1, 1, 1], defaultHeightFromGroundIn3D = 30.0, vertexCount = EDGE_COUNT } = {}) {
|
|
48
|
-
this.id = id;
|
|
49
|
-
this.arcPlugin = new ArcOnTerrainPlugin(id, {
|
|
50
|
-
cameraAttractionIsOn: false,
|
|
51
|
-
vertexCount: vertexCount,
|
|
52
|
-
variativeColorsOn: variativeColorsOn,
|
|
53
|
-
defaultColor: defaultColor,
|
|
54
|
-
defaultHeightFromGroundIn3D: defaultHeightFromGroundIn3D,
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
insertBulk(items) {
|
|
58
|
-
for (const padding of items) {
|
|
59
|
-
this.__delete(padding.key);
|
|
60
|
-
this._memory.set(padding.key, padding);
|
|
61
|
-
}
|
|
62
|
-
const arcInputs = items.flatMap(padding => adapterPadding2Arc(this.globe, padding));
|
|
63
|
-
this.arcPlugin.insertBulk(arcInputs);
|
|
64
|
-
}
|
|
65
|
-
deleteBulk(keys) {
|
|
66
|
-
const arcKeys = keys.flatMap(key => paddingKeys({ key, center: [0, 0], outerRadius: 0, innerRadius: 0, startAngle: 0, coverAngle: 0, stepAngle: 0, color: [0, 0, 0, 1] }));
|
|
67
|
-
this.arcPlugin.deleteBulk(arcKeys);
|
|
68
|
-
}
|
|
69
|
-
updateColor(key, color) {
|
|
70
|
-
// TODO: get all padding keys and update all of them
|
|
71
|
-
if (!this._memory.has(key)) {
|
|
72
|
-
console.warn(`Padding with key ${key} does not exist.`);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const keys = paddingKeys(this._memory.get(key));
|
|
76
|
-
for (let i = 0; i < keys.length; i++) {
|
|
77
|
-
this.arcPlugin.updateColor(keys[i], color);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
__delete(key) {
|
|
81
|
-
const padding = this._memory.get(key);
|
|
82
|
-
if (padding) {
|
|
83
|
-
const keys = paddingKeys(padding);
|
|
84
|
-
this.arcPlugin.deleteBulk(keys);
|
|
85
|
-
this._memory.delete(key);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
init(globe, gl) {
|
|
89
|
-
this.globe = globe;
|
|
90
|
-
this.arcPlugin.init(globe, gl);
|
|
91
|
-
}
|
|
92
|
-
draw3D() {
|
|
93
|
-
this.arcPlugin.draw3D();
|
|
94
|
-
}
|
|
95
|
-
free() {
|
|
96
|
-
if (this.isFreed)
|
|
97
|
-
return;
|
|
98
|
-
this.isFreed = true;
|
|
99
|
-
this.arcPlugin.free();
|
|
100
|
-
}
|
|
101
|
-
}
|