jsgui3-server 0.0.133 → 0.0.135

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/AGENTS.md +6 -0
  2. package/README.md +114 -18
  3. package/TODO.md +81 -0
  4. package/cli.js +96 -0
  5. package/docs/simple-server-api-design.md +702 -0
  6. package/examples/controls/14) window, canvas/client.js +238 -0
  7. package/examples/controls/14) window, canvas/server.js +21 -0
  8. package/examples/controls/14b) window, canvas (improved renderer)/client.js +391 -0
  9. package/examples/controls/14b) window, canvas (improved renderer)/server.js +21 -0
  10. package/examples/controls/14d) window, canvas globe/Clipping.js +0 -0
  11. package/examples/controls/14d) window, canvas globe/EarthGlobeRenderer.js +435 -0
  12. package/examples/controls/14d) window, canvas globe/RenderingPipeline.js +43 -0
  13. package/examples/controls/14d) window, canvas globe/client.js +95 -0
  14. package/examples/controls/14d) window, canvas globe/math.js +76 -0
  15. package/examples/controls/14d) window, canvas globe/pipeline/BaseStage.js +46 -0
  16. package/examples/controls/14d) window, canvas globe/pipeline/ClippingStage.js +29 -0
  17. package/examples/controls/14d) window, canvas globe/pipeline/ComposeStage.js +15 -0
  18. package/examples/controls/14d) window, canvas globe/pipeline/ContinentsStage.js +116 -0
  19. package/examples/controls/14d) window, canvas globe/pipeline/GridStage.js +105 -0
  20. package/examples/controls/14d) window, canvas globe/pipeline/HUDStage.js +10 -0
  21. package/examples/controls/14d) window, canvas globe/pipeline/ShadeSphereStage.js +153 -0
  22. package/examples/controls/14d) window, canvas globe/pipeline/TransformStage.js +23 -0
  23. package/examples/controls/14d) window, canvas globe/pipeline/clipping/FrontFaceStrategy.js +46 -0
  24. package/examples/controls/14d) window, canvas globe/pipeline/clipping/PlaneClipStrategy.js +339 -0
  25. package/examples/controls/14d) window, canvas globe/server.js +21 -0
  26. package/examples/controls/14e) window, canvas multithreaded/client.js +948 -0
  27. package/examples/controls/14e) window, canvas multithreaded/server.js +21 -0
  28. package/examples/controls/14f) window, canvas polyglobe/client.js +569 -0
  29. package/examples/controls/14f) window, canvas polyglobe/math.js +137 -0
  30. package/examples/controls/14f) window, canvas polyglobe/server.js +21 -0
  31. package/module.js +3 -1
  32. package/package.json +12 -6
  33. package/resources/_old_website-resource.js +10 -2
  34. package/resources/jsbuilder/babel/deep_iterate/deep_iterate_babel.js +37 -0
  35. package/serve-factory.js +221 -0
  36. package/serve-helpers.js +95 -0
  37. package/server.js +93 -7
  38. package/tests/cli.test.js +66 -0
  39. package/tests/dummy-client.js +10 -0
  40. package/tests/serve.test.js +210 -0
  41. package/.vscode/settings.json +0 -6
@@ -0,0 +1,948 @@
1
+
2
+
3
+
4
+ const jsgui = require('jsgui3-client');
5
+ const {controls, Control, mixins} = jsgui;
6
+ const {dragable} = mixins;
7
+ const {Checkbox, Date_Picker, Text_Input, Text_Field, Dropdown_Menu} = controls;
8
+ const Active_HTML_Document = require('../../../controls/Active_HTML_Document');
9
+ class EarthGlobeRenderer {
10
+ constructor(canvasOrId, options) {
11
+ if (!options) options = {};
12
+ let cid = canvasOrId || 'globeCanvas';
13
+ let el = typeof cid === 'string' ? document.getElementById(cid) : cid;
14
+ if (!(el instanceof HTMLCanvasElement)) throw new Error("Canvas 'globeCanvas' not found.");
15
+ this.canvas = el;
16
+ this.ctx = this.canvas.getContext('2d', { alpha: true });
17
+
18
+ // ---------- Options / state ----------
19
+ this.opts = {
20
+ padding: options.padding != null ? options.padding : 8,
21
+ background: options.background != null ? options.background : null,
22
+
23
+ // Lighting/material
24
+ baseColor: options.baseColor || [0.13, 0.42, 0.86], // linear-ish RGB
25
+ ambient: options.ambient != null ? options.ambient : 0.08,
26
+ diffuse: options.diffuse != null ? options.diffuse : 1.0,
27
+ specular: options.specular != null ? options.specular : 0.7,
28
+ shininess: options.shininess != null ? options.shininess : 140.0,
29
+ terminatorSoftness: options.terminatorSoftness != null ? options.terminatorSoftness : 0.08,
30
+ atmosphere: options.atmosphere != null ? options.atmosphere : 0.5,
31
+ backlight: options.backlight != null ? options.backlight : 0.25,
32
+
33
+ // Quality
34
+ quality: Math.min(2, Math.max(0.5, options.quality != null ? options.quality : 1.15)),
35
+
36
+ // Grid
37
+ grid: {
38
+ enabled: options.grid && options.grid.enabled != null ? options.grid.enabled : false,
39
+ color: options.grid && options.grid.color ? options.grid.color : '#444',
40
+ lineWidth: options.grid && options.grid.lineWidth != null ? options.grid.lineWidth : 0.8,
41
+ alpha: options.grid && options.grid.alpha != null ? options.grid.alpha : 0.7,
42
+ stepLat: options.grid && options.grid.stepLat != null ? options.grid.stepLat : 10,
43
+ stepLon: options.grid && options.grid.stepLon != null ? options.grid.stepLon : 10,
44
+ sampleStepDeg: options.grid && options.grid.sampleStepDeg != null ? options.grid.sampleStepDeg : 2
45
+ },
46
+
47
+ // Antarctica styling
48
+ antarcticaFill: options.antarcticaFill || 'rgba(220,240,255,0.85)',
49
+ antarcticaStroke: options.antarcticaStroke || 'rgba(180,200,220,0.9)',
50
+ antarcticaLineWidth: options.antarcticaLineWidth != null ? options.antarcticaLineWidth : 1.0,
51
+
52
+ // Inertia
53
+ inertiaFriction: options.inertiaFriction != null ? options.inertiaFriction : 2.6,
54
+ inertiaMinSpeed: options.inertiaMinSpeed != null ? options.inertiaMinSpeed : 0.05,
55
+
56
+ // Workers/tiling
57
+ maxWorkers: options.maxWorkers || (typeof navigator !== 'undefined' ? navigator.hardwareConcurrency || 4 : 4),
58
+
59
+ dpr: (typeof window !== 'undefined' && window.devicePixelRatio) ? window.devicePixelRatio : 1
60
+ };
61
+
62
+ // Sun direction (camera frame)
63
+ this.sun = this._normVec([-0.45, 0.55, 0.65]);
64
+
65
+ // Orientation (arcball) quaternion
66
+ this.q = this._qIdentity();
67
+ this.R = this._mat3FromQuat(this.q);
68
+ this.Rt = this._mat3Transpose(this.R);
69
+
70
+ // Offscreen + internal disc
71
+ this._off = null;
72
+ this._discSize = 0;
73
+ this._Nx = null; this._Ny = null; this._Nz = null; this._A8 = null;
74
+
75
+ // Workers & shared buffers
76
+ this._useWorkers = typeof Worker !== 'undefined';
77
+ this._useSAB = (typeof SharedArrayBuffer !== 'undefined') && (typeof crossOriginIsolated !== 'undefined') && crossOriginIsolated;
78
+ this._workers = [];
79
+ this._rgbaSAB = null;
80
+ this._rgba = null;
81
+ this._imgData = null;
82
+ this._jobSeq = 0;
83
+ this._shadingInProgress = false;
84
+ this._rerenderRequested = false;
85
+
86
+ // Interaction & inertia
87
+ this._dragging = false;
88
+ this._v0 = [0,0,1];
89
+ this._axis = [0,1,0];
90
+ this._omega = 0;
91
+ this._lastTime = 0;
92
+ this._animRAF = 0;
93
+ this._isAnimating = false;
94
+
95
+ // Antarctica outline
96
+ this._ANT = [
97
+ [-70.0, 0.0], [-70.5, 7.5], [-70.3, 15.0], [-70.0, 22.5], [-69.5, 30.0],
98
+ [-69.0, 37.5], [-68.5, 45.0], [-68.0, 52.5], [-67.5, 60.0], [-67.2, 67.5],
99
+ [-66.8, 75.0], [-66.5, 82.5], [-66.3, 90.0], [-66.2, 100.0], [-66.5, 110.0],
100
+ [-66.8, 120.0], [-67.0, 130.0], [-67.5, 140.0], [-68.5, 150.0], [-70.0, 160.0],
101
+ [-73.0, 170.0], [-75.5, 178.0], [-77.5, -175.0], [-78.5, -170.0], [-79.0, -160.0],
102
+ [-78.8, -150.0], [-77.5, -140.0], [-76.5, -130.0], [-75.5, -120.0], [-74.5, -110.0],
103
+ [-73.5, -100.0], [-72.5, -90.0], [-71.8, -80.0], [-70.5, -72.0], [-68.8, -68.0],
104
+ [-66.5, -66.0], [-64.5, -64.0], [-63.0, -60.0], [-64.5, -58.0], [-66.5, -55.0],
105
+ [-70.0, -50.0], [-73.0, -45.0], [-76.0, -42.0], [-78.0, -40.0], [-79.0, -35.0],
106
+ [-78.5, -25.0], [-76.5, -15.0], [-73.5, -7.5], [-71.0, -2.5], [-70.0, 0.0]
107
+ ];
108
+
109
+ this._attachPointerHandlers();
110
+ this.resize();
111
+ this.render();
112
+
113
+ this._onResize = () => { this.resize(); this.render(); };
114
+ window.addEventListener('resize', this._onResize, { passive: true });
115
+ }
116
+
117
+ destroy() {
118
+ window.removeEventListener('resize', this._onResize);
119
+ this._detachPointerHandlers();
120
+ if (this._animRAF) cancelAnimationFrame(this._animRAF);
121
+ for (let i = 0; i < this._workers.length; i++) {
122
+ try { this._workers[i].terminate(); } catch(e){}
123
+ }
124
+ this._workers = [];
125
+ }
126
+
127
+ // ---------- Public controls ----------
128
+ enableGrid(enabled) { this.opts.grid.enabled = !!enabled; this.render(); }
129
+ setGridColor(color) { this.opts.grid.color = color || '#444'; this.render(); }
130
+ setSunDirection(vec3) { this.sun = this._normVec(vec3); this.render(); }
131
+ setSunFromSpherical(lonDeg, latDeg) {
132
+ let toR = Math.PI / 180, lon = lonDeg * toR, lat = latDeg * toR;
133
+ let cx = Math.cos(lat), sx = Math.sin(lat), sz = Math.sin(lon), cz = Math.cos(lon);
134
+ this.setSunDirection([cx*sz, sx, cx*cz]);
135
+ }
136
+ centerOnLatLon(latDeg, lonDeg) {
137
+ let φ = latDeg * Math.PI / 180;
138
+ let λ = lonDeg * Math.PI / 180;
139
+ let qy = this._qFromAxisAngle(0,1,0, -λ);
140
+ let qx = this._qFromAxisAngle(1,0,0, φ);
141
+ this.q = this._qNormalize(this._qMul(qx, qy));
142
+ this._updateRot();
143
+ this.render();
144
+ }
145
+ centerOnAntarctica() { this.centerOnLatLon(-90, 0); }
146
+ setQuality(q) { this.opts.quality = Math.min(2, Math.max(0.5, q || 1)); this._discSize = 0; this.render(); }
147
+
148
+ resize() {
149
+ let dpr = (this.opts.dpr = (typeof window !== 'undefined' && window.devicePixelRatio) ? window.devicePixelRatio : 1);
150
+ let rect = this.canvas.getBoundingClientRect();
151
+ let dw = rect.width || this.canvas.width;
152
+ let dh = rect.height || this.canvas.height;
153
+ let w = Math.max(1, Math.round(dw * dpr));
154
+ let h = Math.max(1, Math.round(dh * dpr));
155
+ if (this.canvas.width !== w || this.canvas.height !== h) {
156
+ this.canvas.width = w; this.canvas.height = h;
157
+ }
158
+ this.ctx.setTransform(1,0,0,1,0,0);
159
+ this.ctx.scale(dpr, dpr);
160
+ }
161
+
162
+ // ---------- Render ----------
163
+ render() {
164
+ if (this._shadingInProgress) { this._rerenderRequested = true; return; }
165
+
166
+ let ctx = this.ctx;
167
+ let rect = this.canvas.getBoundingClientRect();
168
+ let width = rect.width || this.canvas.width / this.opts.dpr;
169
+ let height = rect.height || this.canvas.height / this.opts.dpr;
170
+
171
+ ctx.clearRect(0, 0, width, height);
172
+ if (this.opts.background) {
173
+ ctx.fillStyle = this.opts.background;
174
+ ctx.fillRect(0, 0, width, height);
175
+ }
176
+
177
+ let pad = this.opts.padding;
178
+ let r = Math.max(1, Math.min(width, height) / 2 - pad);
179
+ let cx = width / 2, cy = height / 2;
180
+
181
+ // Placeholder ocean fill so it isn't black while workers run
182
+ let bc = this.opts.baseColor;
183
+ let pr = Math.max(0, Math.min(255, Math.round(Math.pow(bc[0], 1/2.2) * 255)));
184
+ let pg = Math.max(0, Math.min(255, Math.round(Math.pow(bc[1], 1/2.2) * 255)));
185
+ let pb = Math.max(0, Math.min(255, Math.round(Math.pow(bc[2], 1/2.2) * 255)));
186
+ ctx.save();
187
+ ctx.beginPath();
188
+ ctx.arc(cx, cy, r, 0, Math.PI*2);
189
+ ctx.clip();
190
+ ctx.fillStyle = 'rgb(' + pr + ',' + pg + ',' + pb + ')';
191
+ ctx.fillRect(cx - r, cy - r, 2*r, 2*r);
192
+ ctx.restore();
193
+
194
+ // Internal disc size & rotation
195
+ let q = this.opts.quality;
196
+ let d = Math.max(2, Math.floor(2 * r * q));
197
+ this._updateRot();
198
+
199
+ // Ensure disc + workers
200
+ this._ensureDiscAndWorkers(d);
201
+
202
+ // Shade with workers
203
+ this._shadingInProgress = true;
204
+ let jobId = ++this._jobSeq;
205
+
206
+ this._shadeWithWorkers(jobId, {
207
+ Rt: this.Rt,
208
+ sun: this.sun,
209
+ amb: this.opts.ambient,
210
+ kd: this.opts.diffuse,
211
+ ks: this.opts.specular,
212
+ shininess: this.opts.shininess,
213
+ termSoft: this.opts.terminatorSoftness,
214
+ atm: this.opts.atmosphere,
215
+ back: this.opts.backlight,
216
+ baseColor: this.opts.baseColor
217
+ }).then((completedJobId) => {
218
+ if (completedJobId !== this._jobSeq) return; // stale result, ignore
219
+
220
+ let off = this._getOff(d, d);
221
+ off.ctx.putImageData(this._imgData, 0, 0);
222
+
223
+ ctx.save();
224
+ ctx.beginPath();
225
+ ctx.arc(cx, cy, r, 0, Math.PI*2);
226
+ ctx.clip();
227
+ let drawSize = d / this.opts.quality;
228
+ ctx.drawImage(off.canvas, cx - drawSize/2, cy - drawSize/2, drawSize, drawSize);
229
+
230
+ if (this.opts.grid.enabled) this._drawGraticule(cx, cy, r, this._isAnimating ? 2 : 1);
231
+ this._drawAntarctica(cx, cy, r);
232
+ ctx.restore();
233
+
234
+ ctx.beginPath();
235
+ ctx.arc(cx, cy, r, 0, Math.PI*2);
236
+ ctx.strokeStyle = 'rgba(0,0,0,0.14)';
237
+ ctx.lineWidth = 1.1;
238
+ ctx.stroke();
239
+
240
+ this._shadingInProgress = false;
241
+ if (this._rerenderRequested) {
242
+ this._rerenderRequested = false;
243
+ this.render();
244
+ }
245
+ }).catch(() => {
246
+ this._shadingInProgress = false;
247
+ });
248
+ }
249
+
250
+ // ---------- Workers / tiling ----------
251
+ _ensureDiscAndWorkers(d) {
252
+ if (this._discSize !== d) {
253
+ this._buildNormalMap(d);
254
+ this._discSize = d;
255
+
256
+ if (this._useSAB) {
257
+ this._rgbaSAB = new SharedArrayBuffer(d * d * 4);
258
+ this._rgba = new Uint8ClampedArray(this._rgbaSAB);
259
+ this._imgData = new ImageData(this._rgba, d, d);
260
+ } else {
261
+ this._rgbaSAB = null;
262
+ this._rgba = new Uint8ClampedArray(d * d * 4);
263
+ this._imgData = new ImageData(this._rgba, d, d);
264
+ }
265
+ this._initWorkerPool(d);
266
+ } else if (this._workers.length === 0) {
267
+ this._initWorkerPool(d);
268
+ }
269
+ }
270
+
271
+ _initWorkerPool(d) {
272
+ for (let i = 0; i < this._workers.length; i++) {
273
+ try { this._workers[i].terminate(); } catch(e){}
274
+ }
275
+ this._workers = [];
276
+ if (!this._useWorkers) return;
277
+
278
+ let src = this._workerSource();
279
+ let blob = new Blob([src], { type: 'application/javascript' });
280
+ let url = URL.createObjectURL(blob);
281
+
282
+ let N = Math.max(1, Math.min(this.opts.maxWorkers, (typeof navigator !== 'undefined' ? navigator.hardwareConcurrency || 4 : 4)));
283
+ for (let i = 0; i < N; i++) {
284
+ let w = new Worker(url);
285
+ this._workers.push(w);
286
+ }
287
+ // Workers have loaded the code by now
288
+ URL.revokeObjectURL(url);
289
+
290
+ // Prepare and send INIT per worker
291
+ if (this._useSAB) {
292
+ let sabNx = new SharedArrayBuffer(this._Nx.byteLength);
293
+ let sabNy = new SharedArrayBuffer(this._Ny.byteLength);
294
+ let sabNz = new SharedArrayBuffer(this._Nz.byteLength);
295
+ let sabA8 = new SharedArrayBuffer(this._A8.byteLength);
296
+ new Float32Array(sabNx).set(this._Nx);
297
+ new Float32Array(sabNy).set(this._Ny);
298
+ new Float32Array(sabNz).set(this._Nz);
299
+ new Uint8ClampedArray(sabA8).set(this._A8);
300
+
301
+ for (let i = 0; i < this._workers.length; i++) {
302
+ this._workers[i].postMessage({
303
+ type: 'init',
304
+ d,
305
+ useSAB: true,
306
+ sabRGBA: this._rgbaSAB,
307
+ sabNx, sabNy, sabNz, sabA8
308
+ });
309
+ }
310
+ } else {
311
+ // IMPORTANT: clone **per worker**; each transfer detaches the buffer!
312
+ for (let i = 0; i < this._workers.length; i++) {
313
+ let nxBuf = this._Nx.buffer.slice(0);
314
+ let nyBuf = this._Ny.buffer.slice(0);
315
+ let nzBuf = this._Nz.buffer.slice(0);
316
+ let a8Buf = this._A8.buffer.slice(0);
317
+ this._workers[i].postMessage({
318
+ type: 'init',
319
+ d,
320
+ useSAB: false,
321
+ sabRGBA: null,
322
+ sabNx: nxBuf, sabNy: nyBuf, sabNz: nzBuf, sabA8: a8Buf
323
+ }, [nxBuf, nyBuf, nzBuf, a8Buf]);
324
+ }
325
+ }
326
+ }
327
+
328
+ _shadeWithWorkers(jobId, params) {
329
+ if (this._workers.length === 0) {
330
+ return new Promise((resolve) => {
331
+ this._shadeTileOnMain(0, this._discSize, params);
332
+ resolve(jobId);
333
+ });
334
+ }
335
+
336
+ let d = this._discSize;
337
+ let W = this._workers.length;
338
+ let colsPer = Math.floor(d / W);
339
+ let rem = d - colsPer * W;
340
+ let promises = [];
341
+ let self = this;
342
+
343
+ for (let i = 0; i < W; i++) {
344
+ let x0 = i * colsPer + Math.min(i, rem);
345
+ let x1 = x0 + colsPer + (i < rem ? 1 : 0);
346
+
347
+ promises.push(new Promise(function(resolve) {
348
+ let w = self._workers[i];
349
+
350
+ let handler = function(ev) {
351
+ let m = ev.data;
352
+ if (!m || m.jobId !== jobId) return;
353
+ // Non-SAB: stitch returned tile
354
+ if (!self._useSAB && m.rgba) {
355
+ let tile = new Uint8ClampedArray(m.rgba);
356
+ let width = self._discSize;
357
+ let tileW = x1 - x0;
358
+ for (let y = 0; y < width; y++) {
359
+ let dst = (y * width + x0) * 4;
360
+ let src = (y * tileW) * 4;
361
+ self._rgba.set(tile.subarray(src, src + tileW*4), dst);
362
+ }
363
+ }
364
+ w.removeEventListener('message', handler);
365
+ resolve();
366
+ };
367
+ w.addEventListener('message', handler);
368
+
369
+ w.postMessage({
370
+ type: 'shade',
371
+ jobId,
372
+ x0, x1,
373
+ Rt: params.Rt,
374
+ sun: params.sun,
375
+ amb: params.amb, kd: params.kd, ks: params.ks,
376
+ shininess: params.shininess,
377
+ termSoft: params.termSoft, atm: params.atm, back: params.back,
378
+ baseColor: params.baseColor
379
+ });
380
+ }));
381
+ }
382
+
383
+ return Promise.all(promises).then(function(){ return jobId; });
384
+ }
385
+
386
+ // ---------- Single-thread fallback ----------
387
+ _shadeTileOnMain(x0, x1, P) {
388
+ let d = this._discSize;
389
+ let Nx = this._Nx, Ny = this._Ny, Nz = this._Nz, A8 = this._A8;
390
+ let rgba = this._rgba;
391
+
392
+ let Lx = P.sun[0], Ly = P.sun[1], Lz = P.sun[2];
393
+ let Hx = Lx, Hy = Ly, Hz = Lz + 1.0;
394
+ let invH = 1 / Math.hypot(Hx, Hy, Hz);
395
+ Hx *= invH; Hy *= invH; Hz *= invH;
396
+
397
+ let amb = P.amb, kd = P.kd, ks = P.ks, shin = P.shininess;
398
+ let termSoft = P.termSoft, atm = P.atm, back = P.back;
399
+ let baseR = P.baseColor[0], baseG = P.baseColor[1], baseB = P.baseColor[2];
400
+
401
+ let tone = function(c){ return 1 - Math.exp(-2.6*c); };
402
+ let toSRGB = function(l){ if (l<0) l=0; if (l>1) l=1; return Math.pow(l, 1/2.2); };
403
+
404
+ for (let y = 0; y < d; y++) {
405
+ let row = y * d;
406
+ for (let x = x0; x < x1; x++) {
407
+ let i = row + x;
408
+ let nz = Nz[i];
409
+ let p = i<<2;
410
+ if (nz < 0) { rgba[p]=0; rgba[p+1]=0; rgba[p+2]=0; rgba[p+3]=0; continue; }
411
+ let nx = Nx[i], ny = Ny[i];
412
+
413
+ let NL = nx*Lx + ny*Ly + nz*Lz;
414
+ let NV = nz;
415
+ let NH = nx*Hx + ny*Hy + nz*Hz;
416
+
417
+ let t = (NL + termSoft) / (2*termSoft);
418
+ if (t < 0) t = 0; else if (t > 1) t = 1;
419
+ let dayMask = (3 - 2*t) * t * t * (NL > 0 ? NL : 0);
420
+
421
+ let diff = kd * dayMask;
422
+ let spec = NL > 0 ? ks * Math.pow(NH > 0 ? NH : 0, shin) : 0;
423
+
424
+ let rimDay = atm * Math.pow(1 - (NV < 0 ? 0 : (NV > 1 ? 1 : NV)), 2.4) * (NL + 0.15 > 0 ? NL + 0.15 : 0);
425
+ let rimBack = 0;
426
+ if (Lz < 0) {
427
+ let away = NL < 0 ? -NL : 0;
428
+ rimBack = back * Math.pow(1 - (NV < 0 ? 0 : (NV > 1 ? 1 : NV)), 3.2) * away;
429
+ }
430
+
431
+ let rLin = baseR*(amb + diff) + spec + 0.40*rimDay + 0.25*rimBack;
432
+ let gLin = baseG*(amb + diff) + spec + 0.65*rimDay + 0.40*rimBack;
433
+ let bLin = baseB*(amb + diff) + spec + 1.00*rimDay + 0.80*rimBack;
434
+
435
+ rLin = tone(rLin); gLin = tone(gLin); bLin = tone(bLin);
436
+
437
+ rgba[p] = Math.round(toSRGB(rLin)*255);
438
+ rgba[p+1] = Math.round(toSRGB(gLin)*255);
439
+ rgba[p+2] = Math.round(toSRGB(bLin)*255);
440
+ rgba[p+3] = A8[i];
441
+ }
442
+ }
443
+ }
444
+
445
+ // ---------- Worker source ----------
446
+ _workerSource() {
447
+ let s = '';
448
+ s += `
449
+ let d=0, useSAB=false;
450
+ let Nx=null, Ny=null, Nz=null, A8=null;
451
+ let rgba=null;
452
+
453
+ function tone(c){ return 1 - Math.exp(-2.6*c); }
454
+ function toSRGB(l){ if (l<0) l=0; if (l>1) l=1; return Math.pow(l, 1/2.2); }
455
+
456
+ onmessage = function(ev){
457
+ let m = ev.data;
458
+ if (m.type === 'init') {
459
+ d = m.d;
460
+ useSAB = !!m.useSAB;
461
+ if (useSAB) {
462
+ if (m.sabRGBA) rgba = new Uint8ClampedArray(m.sabRGBA);
463
+ Nx = new Float32Array(m.sabNx);
464
+ Ny = new Float32Array(m.sabNy);
465
+ Nz = new Float32Array(m.sabNz);
466
+ A8 = new Uint8ClampedArray(m.sabA8);
467
+ } else {
468
+ Nx = new Float32Array(m.sabNx);
469
+ Ny = new Float32Array(m.sabNy);
470
+ Nz = new Float32Array(m.sabNz);
471
+ A8 = new Uint8ClampedArray(m.sabA8);
472
+ }
473
+ return;
474
+ }
475
+ if (m.type === 'shade') {
476
+ let jobId = m.jobId|0;
477
+ let x0 = m.x0|0, x1 = m.x1|0;
478
+
479
+ let Lx = m.sun[0], Ly = m.sun[1], Lz = m.sun[2];
480
+ let Hx = Lx, Hy = Ly, Hz = Lz + 1.0;
481
+ let invH = 1 / Math.hypot(Hx,Hy,Hz);
482
+ Hx *= invH; Hy *= invH; Hz *= invH;
483
+
484
+ let amb = m.amb, kd = m.kd, ks = m.ks, shin = m.shininess;
485
+ let termSoft = m.termSoft, atm = m.atm, back = m.back;
486
+ let baseR = m.baseColor[0], baseG = m.baseColor[1], baseB = m.baseColor[2];
487
+
488
+ if (useSAB) {
489
+ for (let y=0; y<d; y++) {
490
+ let row = y * d;
491
+ for (let x=x0; x<x1; x++) {
492
+ let i = row + x;
493
+ let nz = Nz[i];
494
+ let p = i<<2;
495
+ if (nz < 0) { rgba[p]=0; rgba[p+1]=0; rgba[p+2]=0; rgba[p+3]=0; continue; }
496
+ let nx = Nx[i], ny = Ny[i];
497
+
498
+ let NL = nx*Lx + ny*Ly + nz*Lz;
499
+ let NV = nz;
500
+ let NH = nx*Hx + ny*Hy + nz*Hz;
501
+
502
+ let t = (NL + termSoft) / (2*termSoft);
503
+ if (t<0) t=0; else if (t>1) t=1;
504
+ let dayMask = (3-2*t)*t*t*(NL>0?NL:0);
505
+
506
+ let diff = kd * dayMask;
507
+ let spec = NL>0 ? ks * Math.pow(NH>0?NH:0, shin) : 0;
508
+
509
+ let rimDay = atm * Math.pow(1 - (NV<0?0:(NV>1?1:NV)), 2.4) * (NL+0.15>0?NL+0.15:0);
510
+ let rimBack = 0;
511
+ if (Lz < 0) {
512
+ let away = NL<0 ? -NL : 0;
513
+ rimBack = back * Math.pow(1 - (NV<0?0:(NV>1?1:NV)), 3.2) * away;
514
+ }
515
+
516
+ let rLin = baseR*(amb + diff) + spec + 0.40*rimDay + 0.25*rimBack;
517
+ let gLin = baseG*(amb + diff) + spec + 0.65*rimDay + 0.40*rimBack;
518
+ let bLin = baseB*(amb + diff) + spec + 1.00*rimDay + 0.80*rimBack;
519
+
520
+ rLin = tone(rLin); gLin = tone(gLin); bLin = tone(bLin);
521
+
522
+ rgba[p] = Math.round(toSRGB(rLin)*255);
523
+ rgba[p+1] = Math.round(toSRGB(gLin)*255);
524
+ rgba[p+2] = Math.round(toSRGB(bLin)*255);
525
+ rgba[p+3] = A8[i];
526
+ }
527
+ }
528
+ postMessage({ jobId });
529
+ } else {
530
+ let tileW = x1 - x0;
531
+ let out = new Uint8ClampedArray(tileW * d * 4);
532
+ for (let y=0; y<d; y++) {
533
+ let row = y * d;
534
+ let base = y * tileW * 4;
535
+ for (let x=x0; x<x1; x++) {
536
+ let i = row + x;
537
+ let nz = Nz[i];
538
+ let p = base + (x - x0) * 4;
539
+ if (nz < 0) { out[p]=0; out[p+1]=0; out[p+2]=0; out[p+3]=0; continue; }
540
+ let nx = Nx[i], ny = Ny[i];
541
+
542
+ let NL = nx*Lx + ny*Ly + nz*Lz;
543
+ let NV = nz;
544
+ let NH = nx*Hx + ny*Hy + nz*Hz;
545
+
546
+ let t = (NL + termSoft) / (2*termSoft);
547
+ if (t<0) t=0; else if (t>1) t=1;
548
+ let dayMask = (3-2*t)*t*t*(NL>0?NL:0);
549
+
550
+ let diff = kd * dayMask;
551
+ let spec = NL>0 ? ks * Math.pow(NH>0?NH:0, shin) : 0;
552
+
553
+ let rimDay = atm * Math.pow(1 - (NV<0?0:(NV>1?1:NV)), 2.4) * (NL+0.15>0?NL+0.15:0);
554
+ let rimBack = 0;
555
+ if (Lz < 0) {
556
+ let away = NL<0 ? -NL : 0;
557
+ rimBack = back * Math.pow(1 - (NV<0?0:(NV>1?1:NV)), 3.2) * away;
558
+ }
559
+
560
+ let rLin = baseR*(amb + diff) + spec + 0.40*rimDay + 0.25*rimBack;
561
+ let gLin = baseG*(amb + diff) + spec + 0.65*rimDay + 0.40*rimBack;
562
+ let bLin = baseB*(amb + diff) + spec + 1.00*rimDay + 0.80*rimBack;
563
+
564
+ rLin = tone(rLin); gLin = tone(gLin); bLin = tone(bLin);
565
+
566
+ out[p] = Math.round(toSRGB(rLin)*255);
567
+ out[p+1] = Math.round(toSRGB(gLin)*255);
568
+ out[p+2] = Math.round(toSRGB(bLin)*255);
569
+ out[p+3] = A8[i];
570
+ }
571
+ }
572
+ postMessage({ jobId, rgba: out.buffer }, [out.buffer]);
573
+ }
574
+ }
575
+ };
576
+ `;
577
+ return s;
578
+ }
579
+
580
+ // ---------- Graticule & Antarctica ----------
581
+ _drawGraticule(cx, cy, r, coarsenFactor) {
582
+ let g = this.opts.grid;
583
+ let ctx = this.ctx;
584
+ let stepLat = Math.max(2, g.stepLat * (coarsenFactor || 1));
585
+ let stepLon = Math.max(2, g.stepLon * (coarsenFactor || 1));
586
+ let sampleStep = Math.max(1, Math.floor(g.sampleStepDeg * (coarsenFactor || 1)));
587
+
588
+ ctx.save();
589
+ ctx.strokeStyle = g.color;
590
+ ctx.globalAlpha = g.alpha;
591
+ ctx.lineWidth = g.lineWidth;
592
+ ctx.lineCap = 'round';
593
+
594
+ for (let lat=-80; lat<=80; lat+=stepLat) this._strokeLatitude(cx, cy, r, lat, sampleStep);
595
+ for (let lon=-180; lon<180; lon+=stepLon) this._strokeLongitude(cx, cy, r, lon, sampleStep);
596
+
597
+ ctx.restore();
598
+ }
599
+
600
+ _drawAntarctica(cx, cy, r) {
601
+ let ctx = this.ctx;
602
+ let allFront = true;
603
+ for (let i=0;i<this._ANT.length;i++){
604
+ let w = this._latLonToXYZ(this._ANT[i][0], this._ANT[i][1]);
605
+ let c = this._applyR(w);
606
+ if (c[2] < 0) { allFront = false; break; }
607
+ }
608
+
609
+ ctx.save();
610
+ ctx.fillStyle = this.opts.antarcticaFill;
611
+ ctx.strokeStyle = this.opts.antarcticaStroke;
612
+ ctx.lineWidth = this.opts.antarcticaLineWidth;
613
+
614
+ if (allFront) {
615
+ ctx.beginPath();
616
+ for (let i=0;i<this._ANT.length;i++){
617
+ let v = this._projectLatLon(this._ANT[i][0], this._ANT[i][1]); if (!v) continue;
618
+ let x = cx + r*v[0], y = cy - r*v[1];
619
+ if (i===0) ctx.moveTo(x,y); else ctx.lineTo(x,y);
620
+ }
621
+ ctx.closePath();
622
+ ctx.fill(); ctx.stroke();
623
+ } else {
624
+ let runs = this._densifyAndSplitFrontRuns(this._ANT, 1.2);
625
+ for (let k=0;k<runs.length;k++){
626
+ let run = runs[k]; if (run.length<2) continue;
627
+ ctx.beginPath();
628
+ for (let i=0;i<run.length;i++){
629
+ let v = this._projectLatLon(run[i][0], run[i][1]); if (!v) continue;
630
+ let x = cx + r*v[0], y = cy - r*v[1];
631
+ if (i===0) ctx.moveTo(x,y); else ctx.lineTo(x,y);
632
+ }
633
+ ctx.closePath();
634
+ ctx.fill(); ctx.stroke();
635
+ }
636
+ }
637
+ ctx.restore();
638
+ }
639
+
640
+ _strokeLatitude(cx, cy, r, latDeg, stepDeg) {
641
+ let ctx = this.ctx;
642
+ ctx.beginPath();
643
+ let drawing = false;
644
+ for (let lon=-180; lon<=180; lon+=stepDeg) {
645
+ let v = this._projectLatLon(latDeg, lon);
646
+ if (!v) { drawing = false; continue; }
647
+ let x = cx + r*v[0], y = cy - r*v[1];
648
+ if (!drawing) { ctx.moveTo(x,y); drawing=true; } else ctx.lineTo(x,y);
649
+ }
650
+ ctx.stroke();
651
+ }
652
+ _strokeLongitude(cx, cy, r, lonDeg, stepDeg) {
653
+ let ctx = this.ctx;
654
+ ctx.beginPath();
655
+ let drawing = false;
656
+ for (let lat=-90; lat<=90; lat+=stepDeg) {
657
+ let v = this._projectLatLon(lat, lonDeg);
658
+ if (!v) { drawing = false; continue; }
659
+ let x = cx + r*v[0], y = cy - r*v[1];
660
+ if (!drawing) { ctx.moveTo(x,y); drawing=true; } else ctx.lineTo(x,y);
661
+ }
662
+ ctx.stroke();
663
+ }
664
+
665
+ _densifyAndSplitFrontRuns(latLonPts, stepDeg) {
666
+ let out = [];
667
+ let pts = latLonPts.slice(0);
668
+ if (pts[0][0] !== pts[pts.length-1][0] || pts[0][1] !== pts[pts.length-1][1]) {
669
+ pts.push([pts[0][0], pts[0][1]]);
670
+ }
671
+ let run = [];
672
+ for (let i=0;i<pts.length-1;i++){
673
+ let a=pts[i], b=pts[i+1];
674
+ let seg=this._interpLL(a,b,stepDeg);
675
+ for (let j=0;j<seg.length;j++){
676
+ let W=this._latLonToXYZ(seg[j][0], seg[j][1]);
677
+ let C=this._applyR(W);
678
+ if (C[2]>=0) run.push([seg[j][0], seg[j][1]]);
679
+ else { if (run.length>2) out.push(run); run=[]; }
680
+ }
681
+ }
682
+ if (run.length>2) out.push(run);
683
+ return out;
684
+ }
685
+ _interpLL(a,b,stepDeg){
686
+ let latA=a[0], lonA=a[1], latB=b[0], lonB=b[1];
687
+ let dLon = lonB - lonA; if (dLon>180) dLon-=360; if (dLon<-180) dLon+=360;
688
+ let dLat = latB - latA;
689
+ let dist = Math.hypot(dLat, dLon);
690
+ let steps = Math.max(1, Math.ceil(dist / stepDeg));
691
+ let out = [];
692
+ for (let i=0;i<steps;i++){
693
+ let t = i/steps;
694
+ out.push([latA + dLat*t, lonA + dLon*t]);
695
+ }
696
+ return out;
697
+ }
698
+
699
+ _projectLatLon(latDeg, lonDeg) {
700
+ let v = this._latLonToXYZ(latDeg, lonDeg);
701
+ let c = this._applyR(v);
702
+ if (c[2] < 0) return null;
703
+ return [c[0], c[1]];
704
+ }
705
+ _latLonToXYZ(latDeg, lonDeg){
706
+ let φ = latDeg * Math.PI / 180, λ = lonDeg * Math.PI / 180;
707
+ let cφ = Math.cos(φ), sφ = Math.sin(φ);
708
+ let sλ = Math.sin(λ), cλ = Math.cos(λ);
709
+ return [cφ*sλ, sφ, cφ*cλ];
710
+ }
711
+
712
+ // ---------- Unit-disc normal map ----------
713
+ _buildNormalMap(d) {
714
+ let rad = d * 0.5, invR = 1 / rad;
715
+ this._Nx = new Float32Array(d*d);
716
+ this._Ny = new Float32Array(d*d);
717
+ this._Nz = new Float32Array(d*d);
718
+ this._A8 = new Uint8ClampedArray(d*d);
719
+
720
+ let i = 0;
721
+ for (let y=0; y<d; y++) {
722
+ let vy_screen = (y + 0.5 - rad) * invR;
723
+ for (let x=0; x<d; x++) {
724
+ let vx = (x + 0.5 - rad) * invR;
725
+ let vy = -vy_screen;
726
+ let rr = vx*vx + vy*vy;
727
+ if (rr > 1.0005) {
728
+ this._Nz[i] = -1;
729
+ this._A8[i] = 0;
730
+ } else {
731
+ let dist = Math.sqrt(rr);
732
+ let vz = Math.sqrt(1 - (rr < 0 ? 0 : rr));
733
+ this._Nx[i] = vx; this._Ny[i] = vy; this._Nz[i] = vz;
734
+ let aa = (1 - dist) * rad; if (aa < 0) aa = 0; if (aa > 1) aa = 1;
735
+ this._A8[i] = Math.round(255 * aa);
736
+ }
737
+ i++;
738
+ }
739
+ }
740
+ }
741
+
742
+ // ---------- Offscreen ----------
743
+ _getOff(w, h) {
744
+ if (!this._off || this._off.canvas.width !== w || this._off.canvas.height !== h) {
745
+ let c = document.createElement('canvas'); c.width = w; c.height = h;
746
+ this._off = { canvas: c, ctx: c.getContext('2d') };
747
+ }
748
+ return this._off;
749
+ }
750
+
751
+ // ---------- Arcball + inertia ----------
752
+ _attachPointerHandlers() {
753
+ let el = this.canvas;
754
+ let s = getComputedStyle(el);
755
+ if (s.touchAction !== 'none') el.style.touchAction = 'none';
756
+ let onDown = (ev) => {
757
+ this._dragging = true; this._isAnimating = true;
758
+ this._v0 = this._screenToArcball(ev.clientX, ev.clientY);
759
+ this._omega = 0; this._lastTime = performance.now();
760
+ try { el.setPointerCapture(ev.pointerId); } catch(e){}
761
+ };
762
+ let onMove = (ev) => {
763
+ if (!this._dragging) return;
764
+ let v1 = this._screenToArcball(ev.clientX, ev.clientY);
765
+ let dq = this._qFromVectors(this._v0, v1);
766
+ this.q = this._qNormalize(this._qMul(dq, this.q));
767
+ this._v0 = v1;
768
+
769
+ let now = performance.now(), dt = Math.max(1, now - this._lastTime) / 1000;
770
+ this._lastTime = now;
771
+
772
+ let angle = 2 * Math.acos(Math.max(-1, Math.min(1, dq[3])));
773
+ if (angle > Math.PI) angle = 2*Math.PI - angle;
774
+ let s = Math.sqrt(Math.max(0, 1 - dq[3]*dq[3]));
775
+ if (s < 1e-6) { this._axis[0]=0; this._axis[1]=1; this._axis[2]=0; }
776
+ else { this._axis[0]=dq[0]/s; this._axis[1]=dq[1]/s; this._axis[2]=dq[2]/s; }
777
+ this._omega = angle / dt;
778
+
779
+ this._updateRot();
780
+ this.render();
781
+ };
782
+ let onUp = (ev) => {
783
+ this._dragging = false;
784
+ try { el.releasePointerCapture(ev.pointerId); } catch(e){}
785
+ this._startInertia();
786
+ };
787
+ el.addEventListener('pointerdown', onDown);
788
+ el.addEventListener('pointermove', onMove);
789
+ el.addEventListener('pointerup', onUp);
790
+ el.addEventListener('pointercancel', onUp);
791
+ this._ptr = { onDown, onMove, onUp };
792
+ }
793
+ _detachPointerHandlers() {
794
+ if (!this._ptr) return;
795
+ let el = this.canvas;
796
+ el.removeEventListener('pointerdown', this._ptr.onDown);
797
+ el.removeEventListener('pointermove', this._ptr.onMove);
798
+ el.removeEventListener('pointerup', this._ptr.onUp);
799
+ el.removeEventListener('pointercancel', this._ptr.onUp);
800
+ this._ptr = null;
801
+ }
802
+ _startInertia() {
803
+ if (this._omega <= this.opts.inertiaMinSpeed) { this._omega = 0; this._isAnimating = false; this.render(); return; }
804
+ let friction = this.opts.inertiaFriction;
805
+ let last = performance.now();
806
+ let step = (now) => {
807
+ let dt = Math.max(1, now - last) / 1000; last = now;
808
+ this._omega *= Math.exp(-friction * dt);
809
+ if (this._omega <= this.opts.inertiaMinSpeed) {
810
+ this._omega = 0; this._isAnimating = false; this._animRAF = 0; this.render(); return;
811
+ }
812
+ let dq = this._qFromAxisAngle(this._axis[0], this._axis[1], this._axis[2], this._omega * dt);
813
+ this.q = this._qNormalize(this._qMul(dq, this.q));
814
+ this._updateRot();
815
+ this.render();
816
+ this._animRAF = requestAnimationFrame(step);
817
+ };
818
+ if (this._animRAF) cancelAnimationFrame(this._animRAF);
819
+ this._animRAF = requestAnimationFrame(step);
820
+ }
821
+ _screenToArcball(clientX, clientY) {
822
+ let rect = this.canvas.getBoundingClientRect();
823
+ let cx = rect.left + rect.width / 2;
824
+ let cy = rect.top + rect.height / 2;
825
+ let pad = this.opts.padding;
826
+ let r = Math.max(1, Math.min(rect.width, rect.height)/2 - pad);
827
+ let x = (clientX - cx) / r;
828
+ let y = (cy - clientY) / r;
829
+ let d2 = x*x + y*y;
830
+ if (d2 <= 1) return [x, y, Math.sqrt(1 - d2)];
831
+ let inv = 1 / Math.sqrt(d2);
832
+ return [x*inv, y*inv, 0];
833
+ }
834
+
835
+ // ---------- Math ----------
836
+ _updateRot() { this.R = this._mat3FromQuat(this.q); this.Rt = this._mat3Transpose(this.R); }
837
+ _applyR(v) {
838
+ let R = this.R;
839
+ return [ R[0]*v[0] + R[3]*v[1] + R[6]*v[2],
840
+ R[1]*v[0] + R[4]*v[1] + R[7]*v[2],
841
+ R[2]*v[0] + R[5]*v[1] + R[8]*v[2] ];
842
+ }
843
+ _qIdentity() { return [0,0,0,1]; }
844
+ _qNormalize(q){ let x=q[0],y=q[1],z=q[2],w=q[3]; let n=Math.hypot(x,y,z,w)||1; q[0]=x/n;q[1]=y/n;q[2]=z/n;q[3]=w/n; return q; }
845
+ _qMul(a,b){ return [ a[3]*b[0] + a[0]*b[3] + a[1]*b[2] - a[2]*b[1],
846
+ a[3]*b[1] - a[0]*b[2] + a[1]*b[3] + a[2]*b[0],
847
+ a[3]*b[2] + a[0]*b[1] - a[1]*b[0] + a[2]*b[3],
848
+ a[3]*b[3] - a[0]*b[0] - a[1]*b[1] - a[2]*b[2] ]; }
849
+ _qFromAxisAngle(ax,ay,az,angle){ let n=Math.hypot(ax,ay,az)||1; let s=Math.sin(angle*0.5)/n; return [ax*s,ay*s,az*s,Math.cos(angle*0.5)]; }
850
+ _qFromVectors(u,v){
851
+ let dot=u[0]*v[0]+u[1]*v[1]+u[2]*v[2];
852
+ if (dot>=1-1e-10) return [0,0,0,1];
853
+ if (dot<=-1+1e-10){ let ax=Math.abs(u[0])<0.9?1:0, ay=Math.abs(u[1])<0.9?1:0, az=Math.abs(u[2])<0.9?1:0;
854
+ let cx=u[1]*az-u[2]*ay, cy=u[2]*ax-u[0]*az, cz=u[0]*ay-u[1]*ax; return this._qNormalize([cx,cy,cz,0]); }
855
+ let cx=u[1]*v[2]-u[2]*v[1], cy=u[2]*v[0]-u[0]*v[2], cz=u[0]*v[1]-u[1]*v[0], w=1+dot;
856
+ return this._qNormalize([cx,cy,cz,w]);
857
+ }
858
+ _mat3FromQuat(q){
859
+ let x=q[0],y=q[1],z=q[2],w=q[3],x2=x+x,y2=y+y,z2=z+z,xx=x*x2,yy=y*y2,zz=z*z2,xy=x*y2,xz=x*z2,yz=y*z2,wx=w*x2,wy=w*y2,wz=w*z2;
860
+ return [1-(yy+zz), xy+wz, xz-wy, xy-wz, 1-(xx+zz), yz+wx, xz+wy, yz-wx, 1-(xx+yy)];
861
+ }
862
+ _mat3Transpose(m){ return [m[0],m[3],m[6], m[1],m[4],m[7], m[2],m[5],m[8]]; }
863
+ _normVec(v){ let n=Math.hypot(v[0],v[1],v[2])||1; return [v[0]/n, v[1]/n, v[2]/n]; }
864
+ }
865
+ class Demo_UI extends Active_HTML_Document {
866
+ constructor(spec = {}) {
867
+ spec.__type_name = spec.__type_name || 'demo_ui';
868
+ super(spec);
869
+ const {context} = this;
870
+ if (typeof this.body.add_class === 'function') {
871
+ this.body.add_class('demo-ui');
872
+ }
873
+ const compose = () => {
874
+ const window = new controls.Window({
875
+ context: context,
876
+ title: 'jsgui3-html Dropdown_Menu',
877
+ pos: [5, 5]
878
+ });
879
+ window.size = [1000, 1000];
880
+ const canvas = new controls.canvas({
881
+ context
882
+ });
883
+ canvas.dom.attributes.id = 'globeCanvas'
884
+ canvas.size = [900, 900];
885
+ window.inner.add(canvas);
886
+ this.body.add(window);
887
+ this._ctrl_fields = this._ctrl_fields || {};
888
+ this._ctrl_fields.canvas = this.canvas = canvas;
889
+ }
890
+ if (!spec.el) {
891
+ compose();
892
+ //this.add_change_listeners();
893
+ }
894
+ }
895
+ /*
896
+ add_change_listeners() {
897
+ const {select_options} = this;
898
+ select_options.data.model.on('change', e => {
899
+ console.log('select_options.data.model change e', e);
900
+ });
901
+ }
902
+ */
903
+ activate() {
904
+ if (!this.__active) {
905
+ super.activate();
906
+ const {context, ti1, ti2} = this;
907
+ //this.add_change_listeners();
908
+ console.log('activate Demo_UI');
909
+ context.on('window-resize', e_resize => {
910
+ });
911
+
912
+ /*
913
+ const globe = new EarthGlobeRenderer("globeCanvas", {
914
+ background: "#081019",
915
+ quality: 1.25,
916
+ grid: { enabled: true, color: "#444" }, // dark grey default
917
+ inertiaFriction: 2.8,
918
+ zoom: 1
919
+ });
920
+
921
+ // Place sun front-left-up:
922
+ globe.setSunFromSpherical(-35, 25);
923
+ */
924
+
925
+ let globe = new EarthGlobeRenderer('globeCanvas', {
926
+ background: '#081019',
927
+ quality: 1.2,
928
+ grid: { enabled: true, color: '#444' } // dark grey default
929
+ });
930
+ globe.setSunFromSpherical(-35, 25);
931
+ }
932
+ }
933
+ }
934
+ Demo_UI.css = `
935
+ * {
936
+ margin: 0;
937
+ padding: 0;
938
+ }
939
+ body {
940
+ overflow-x: hidden;
941
+ overflow-y: hidden;
942
+ background-color: #E0E0E0;
943
+ }
944
+ .demo-ui {
945
+ }
946
+ `;
947
+ controls.Demo_UI = Demo_UI;
948
+ module.exports = jsgui;