jsgui3-server 0.0.133 → 0.0.134

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.
@@ -1,6 +1,13 @@
1
1
  {
2
2
  "workbench.colorCustomizations": {
3
3
  "minimap.background": "#00000000",
4
- "scrollbar.shadow": "#00000000"
4
+ "scrollbar.shadow": "#00000000",
5
+ "editor.defaultFormatter": "rvest.vs-code-prettier-eslint",
6
+ "editor.formatOnType": false, // required
7
+ "editor.formatOnPaste": true, // optional
8
+ "editor.formatOnSave": true, // optional
9
+ "editor.formatOnSaveMode": "file", // required to format on save
10
+ "files.autoSave": "onFocusChange", // optional but recommended
11
+ "vs-code-prettier-eslint.prettierLast": false // set as "true" to run 'prettier' last not first
5
12
  }
6
13
  }
@@ -0,0 +1,238 @@
1
+ const jsgui = require('jsgui3-client');
2
+ const {controls, Control, mixins} = jsgui;
3
+ const {dragable} = mixins;
4
+ const {Checkbox, Date_Picker, Text_Input, Text_Field, Dropdown_Menu} = controls;
5
+ const Active_HTML_Document = require('../../../controls/Active_HTML_Document');
6
+
7
+
8
+
9
+ class BlueSphereRenderer {
10
+ constructor(canvas, opts = {}) {
11
+ this.canvas =
12
+ typeof canvas === "string" ? document.getElementById(canvas) : canvas;
13
+ if (!(this.canvas instanceof HTMLCanvasElement)) {
14
+ throw new Error("Pass a canvas element or its id");
15
+ }
16
+
17
+ this.ctx = this.canvas.getContext("2d");
18
+ this.opts = {
19
+ padding: opts.padding ?? 8,
20
+ color: opts.color ?? "#2d7fff", // base blue
21
+ background: opts.background ?? null, // e.g. "#0a0f1e" or null for transparent
22
+ dpr: window.devicePixelRatio || 1
23
+ };
24
+
25
+ this.resize();
26
+ this.render();
27
+
28
+ // Re-render automatically on viewport resizes.
29
+ this._onResize = () => { this.resize(); this.render(); };
30
+ window.addEventListener("resize", this._onResize, { passive: true });
31
+ }
32
+
33
+ destroy() {
34
+ window.removeEventListener("resize", this._onResize);
35
+ }
36
+
37
+ setColor(hex) {
38
+ this.opts.color = hex;
39
+ this.render();
40
+ }
41
+
42
+ resize() {
43
+ const dpr = (this.opts.dpr = window.devicePixelRatio || 1);
44
+ const rect = this.canvas.getBoundingClientRect();
45
+
46
+ // If CSS controls the canvas size, use that; otherwise fall back to attributes.
47
+ const displayWidth = rect.width || this.canvas.width;
48
+ const displayHeight = rect.height || this.canvas.height;
49
+
50
+ const w = Math.max(1, Math.round(displayWidth * dpr));
51
+ const h = Math.max(1, Math.round(displayHeight * dpr));
52
+
53
+ if (this.canvas.width !== w || this.canvas.height !== h) {
54
+ this.canvas.width = w;
55
+ this.canvas.height = h;
56
+ }
57
+
58
+ // Scale the drawing coordinates back to CSS pixels.
59
+ this.ctx.setTransform(1, 0, 0, 1, 0, 0);
60
+ this.ctx.scale(dpr, dpr);
61
+ }
62
+
63
+ render() {
64
+ const ctx = this.ctx;
65
+
66
+ const rect = this.canvas.getBoundingClientRect();
67
+ const width = rect.width || this.canvas.width / this.opts.dpr;
68
+ const height = rect.height || this.canvas.height / this.opts.dpr;
69
+
70
+ // Clear / background
71
+ ctx.clearRect(0, 0, width, height);
72
+ if (this.opts.background) {
73
+ ctx.fillStyle = this.opts.background;
74
+ ctx.fillRect(0, 0, width, height);
75
+ }
76
+
77
+ const pad = this.opts.padding;
78
+ const r = Math.max(1, Math.min(width, height) / 2 - pad);
79
+ const cx = width / 2;
80
+ const cy = height / 2;
81
+
82
+ // Clip to a circle so the gradient stays perfectly round.
83
+ ctx.save();
84
+ ctx.beginPath();
85
+ ctx.arc(cx, cy, r, 0, Math.PI * 2);
86
+ ctx.clip();
87
+
88
+ // Light from top-left: bright highlight near that side, darker on the rim.
89
+ const hx = cx - r * 0.4;
90
+ const hy = cy - r * 0.45;
91
+ const grad = ctx.createRadialGradient(hx, hy, r * 0.15, cx, cy, r);
92
+
93
+ const base = this.opts.color;
94
+ grad.addColorStop(0.0, this._tint(base, 0.35)); // bright highlight
95
+ grad.addColorStop(0.5, base); // mid
96
+ grad.addColorStop(1.0, this._shade(base, 0.45)); // dark rim
97
+
98
+ ctx.fillStyle = grad;
99
+ ctx.fillRect(cx - r, cy - r, r * 2, r * 2);
100
+ ctx.restore();
101
+
102
+ // Soft rim outline
103
+ ctx.beginPath();
104
+ ctx.arc(cx, cy, r, 0, Math.PI * 2);
105
+ ctx.strokeStyle = "rgba(0,0,0,0.15)";
106
+ ctx.lineWidth = 1.5;
107
+ ctx.stroke();
108
+
109
+ // Subtle glossy specular highlight
110
+ ctx.save();
111
+ ctx.globalAlpha = 0.18;
112
+ ctx.beginPath();
113
+ ctx.ellipse(cx - r * 0.35, cy - r * 0.43, r * 0.25, r * 0.18, -0.35, 0, Math.PI * 2);
114
+ const gloss = ctx.createRadialGradient(
115
+ cx - r * 0.35, cy - r * 0.43, 0,
116
+ cx - r * 0.35, cy - r * 0.43, r * 0.25
117
+ );
118
+ gloss.addColorStop(0, "rgba(255,255,255,0.9)");
119
+ gloss.addColorStop(1, "rgba(255,255,255,0)");
120
+ ctx.fillStyle = gloss;
121
+ ctx.fill();
122
+ ctx.restore();
123
+ }
124
+
125
+ // ---- helpers: simple HSL lighten/darken from a hex ----
126
+
127
+ _tint(hex, amount) { // amount in [0..1]
128
+ const { h, s, l } = this._hexToHSL(hex);
129
+ return this._hslToCSS(h, s, Math.min(100, l + amount * 100));
130
+ }
131
+
132
+ _shade(hex, amount) {
133
+ const { h, s, l } = this._hexToHSL(hex);
134
+ return this._hslToCSS(h, s, Math.max(0, l - amount * 100));
135
+ }
136
+
137
+ _hexToHSL(hex) {
138
+ let c = hex.replace("#", "");
139
+ if (c.length === 3) c = [...c].map(x => x + x).join("");
140
+
141
+ const r = parseInt(c.slice(0, 2), 16) / 255;
142
+ const g = parseInt(c.slice(2, 4), 16) / 255;
143
+ const b = parseInt(c.slice(4, 6), 16) / 255;
144
+
145
+ const max = Math.max(r, g, b), min = Math.min(r, g, b);
146
+ let h, s, l = (max + min) / 2;
147
+
148
+ if (max === min) {
149
+ h = 0; s = 0;
150
+ } else {
151
+ const d = max - min;
152
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
153
+ switch (max) {
154
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
155
+ case g: h = (b - r) / d + 2; break;
156
+ case b: h = (r - g) / d + 4; break;
157
+ }
158
+ h /= 6;
159
+ }
160
+
161
+ return { h: h * 360, s: s * 100, l: l * 100 };
162
+ }
163
+
164
+ _hslToCSS(h, s, l) {
165
+ return `hsl(${h.toFixed(1)} ${s.toFixed(1)}% ${l.toFixed(1)}%)`;
166
+ }
167
+ }
168
+
169
+ class Demo_UI extends Active_HTML_Document {
170
+ constructor(spec = {}) {
171
+ spec.__type_name = spec.__type_name || 'demo_ui';
172
+ super(spec);
173
+ const {context} = this;
174
+ if (typeof this.body.add_class === 'function') {
175
+ this.body.add_class('demo-ui');
176
+ }
177
+ const compose = () => {
178
+ const window = new controls.Window({
179
+ context: context,
180
+ title: 'jsgui3-html Dropdown_Menu',
181
+ pos: [5, 5]
182
+ });
183
+ window.size = [480, 400];
184
+ const canvas = new controls.canvas({
185
+ context
186
+ });
187
+ canvas.dom.attributes.id = 'globeCanvas'
188
+ canvas.size = [300, 300];
189
+ window.inner.add(canvas);
190
+ this.body.add(window);
191
+ this._ctrl_fields = this._ctrl_fields || {};
192
+ this._ctrl_fields.canvas = this.canvas = canvas;
193
+ }
194
+ if (!spec.el) {
195
+ compose();
196
+ //this.add_change_listeners();
197
+ }
198
+ }
199
+ /*
200
+ add_change_listeners() {
201
+ const {select_options} = this;
202
+ select_options.data.model.on('change', e => {
203
+ console.log('select_options.data.model change e', e);
204
+ });
205
+ }
206
+ */
207
+ activate() {
208
+ if (!this.__active) {
209
+ super.activate();
210
+ const {context, ti1, ti2} = this;
211
+ //this.add_change_listeners();
212
+ console.log('activate Demo_UI');
213
+ context.on('window-resize', e_resize => {
214
+ });
215
+
216
+ const globe = new BlueSphereRenderer("globeCanvas", {
217
+ color: "#1e88e5", // try different blues
218
+ background: null, // or a color like "#0b1020"
219
+ padding: 10
220
+ });
221
+ }
222
+ }
223
+ }
224
+ Demo_UI.css = `
225
+ * {
226
+ margin: 0;
227
+ padding: 0;
228
+ }
229
+ body {
230
+ overflow-x: hidden;
231
+ overflow-y: hidden;
232
+ background-color: #E0E0E0;
233
+ }
234
+ .demo-ui {
235
+ }
236
+ `;
237
+ controls.Demo_UI = Demo_UI;
238
+ module.exports = jsgui;
@@ -0,0 +1,21 @@
1
+ const jsgui = require('./client');
2
+ const {Demo_UI} = jsgui.controls;
3
+ const Server = require('../../../server');
4
+ if (require.main === module) {
5
+ const server = new Server({
6
+ Ctrl: Demo_UI,
7
+ debug: true,
8
+ 'src_path_client_js': require.resolve('./client.js'),
9
+ });
10
+ console.log('waiting for server ready event');
11
+ server.one('ready', () => {
12
+ console.log('server ready');
13
+ server.start(52000, function (err, cb_start) {
14
+ if (err) {
15
+ throw err;
16
+ } else {
17
+ console.log('server started');
18
+ }
19
+ });
20
+ })
21
+ }
@@ -0,0 +1,391 @@
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
+
10
+
11
+ // EarthLikeSphereRenderer.js
12
+ // Renders a blue "Earth-like" sphere with sunlit shading and ocean glint.
13
+ // - Proper diffuse + specular lighting from a 3D sun direction vector
14
+ // - Soft day/night terminator
15
+ // - Atmospheric rim, including backlit twilight when the sun is behind Earth
16
+ // - High-DPI aware, resizes with the canvas element
17
+
18
+ class EarthLikeSphereRenderer {
19
+ constructor(canvas, opts = {}) {
20
+ this.canvas =
21
+ typeof canvas === "string" ? document.getElementById(canvas) : canvas;
22
+ if (!(this.canvas instanceof HTMLCanvasElement)) {
23
+ throw new Error("Pass a canvas element or its id");
24
+ }
25
+
26
+ this.ctx = this.canvas.getContext("2d");
27
+
28
+ // Lighting / material defaults tuned for "ocean Earth" look
29
+ this.opts = {
30
+ padding: opts.padding ?? 8,
31
+ background: opts.background ?? null, // e.g. "#071018" or null
32
+ baseColor: opts.baseColor ?? [0.13, 0.42, 0.86], // ocean-ish RGB (linear-ish 0..1)
33
+ ambient: opts.ambient ?? 0.08, // base floor light
34
+ diffuse: opts.diffuse ?? 1.0, // lambertian strength
35
+ specular: opts.specular ?? 0.75, // specular strength (ocean glint)
36
+ shininess: opts.shininess ?? 120.0, // specular tightness (higher = tighter)
37
+ terminatorSoftness: opts.terminatorSoftness ?? 0.08, // soften day/night edge
38
+ atmosphere: opts.atmosphere ?? 0.45, // day-side rim
39
+ backlight: opts.backlight ?? 0.22, // night-side backscatter (sun behind)
40
+ dpr: window.devicePixelRatio || 1,
41
+ quality: Math.min(2, Math.max(0.5, opts.quality ?? 1.0)), // internal sampling scale
42
+ };
43
+
44
+ // Default sun direction (unit; toward viewer is +Z). This is "above-left-front".
45
+ this.sun = this._normalize([ -0.45, 0.55, 0.65 ]);
46
+
47
+ this.resize();
48
+ this.render();
49
+
50
+ this._onResize = () => { this.resize(); this.render(); };
51
+ window.addEventListener("resize", this._onResize, { passive: true });
52
+ }
53
+
54
+ destroy() {
55
+ window.removeEventListener("resize", this._onResize);
56
+ }
57
+
58
+ // ---- Public controls ------------------------------------------------------
59
+
60
+ /**
61
+ * Set sun direction as a unit vector [x, y, z] in camera coordinates.
62
+ * Axes: +X right, +Y up, +Z toward camera (viewer).
63
+ * Example: front-left-up is [-0.5, 0.5, 0.7]
64
+ */
65
+ setSunDirection(vec3) {
66
+ this.sun = this._normalize(vec3);
67
+ this.render();
68
+ }
69
+
70
+ /**
71
+ * Convenience: set sun from spherical angles (degrees).
72
+ * lonDeg: 0° is toward camera (+Z), +90° is to the right (+X), ±180° is away (-Z)
73
+ * latDeg: +90° straight up (+Y), -90° straight down (-Y)
74
+ */
75
+ setSunFromSpherical(lonDeg, latDeg) {
76
+ const toRad = Math.PI / 180;
77
+ const lon = lonDeg * toRad;
78
+ const lat = latDeg * toRad;
79
+ const cx = Math.cos(lat), sx = Math.sin(lat);
80
+ const sz = Math.sin(lon), cz = Math.cos(lon);
81
+ this.setSunDirection([ cx * sz, sx, cx * cz ]);
82
+ }
83
+
84
+ /**
85
+ * Convenience: set sun from azimuth/elevation (degrees) in camera frame.
86
+ * azimuthDeg: 0° = straight toward camera (+Z), 90° = right (+X), 180° = away (-Z)
87
+ * elevationDeg: 0° = horizon plane, +90° = straight up (+Y)
88
+ */
89
+ setSunFromAzEl(azimuthDeg, elevationDeg) {
90
+ this.setSunFromSpherical(azimuthDeg, elevationDeg);
91
+ }
92
+
93
+ /**
94
+ * Optional helper: very rough astronomical position -> direction in an
95
+ * inertial, equatorial-like frame (J2000-ish). For realism you’ll typically
96
+ * rotate this into your camera frame. Good enough as a starting point.
97
+ */
98
+ setSunApproxFromDateUTC(dateUtc /* Date */) {
99
+ const vEci = this._approxSunEciUnit(dateUtc);
100
+ // Default assumption: camera frame aligned with ECI, viewer on +Z.
101
+ // If you have your own camera orientation, rotate vEci accordingly before setSunDirection.
102
+ this.setSunDirection(vEci);
103
+ }
104
+
105
+ // ---- Canvas sizing --------------------------------------------------------
106
+
107
+ resize() {
108
+ const dpr = (this.opts.dpr = window.devicePixelRatio || 1);
109
+ const rect = this.canvas.getBoundingClientRect();
110
+ const displayWidth = rect.width || this.canvas.width;
111
+ const displayHeight = rect.height || this.canvas.height;
112
+
113
+ const w = Math.max(1, Math.round(displayWidth * dpr));
114
+ const h = Math.max(1, Math.round(displayHeight * dpr));
115
+
116
+ if (this.canvas.width !== w || this.canvas.height !== h) {
117
+ this.canvas.width = w;
118
+ this.canvas.height = h;
119
+ }
120
+
121
+ // Scale so drawing uses CSS pixels.
122
+ this.ctx.setTransform(1, 0, 0, 1, 0, 0);
123
+ this.ctx.scale(dpr, dpr);
124
+ }
125
+
126
+ // ---- Rendering ------------------------------------------------------------
127
+
128
+ render() {
129
+ const ctx = this.ctx;
130
+
131
+ const rect = this.canvas.getBoundingClientRect();
132
+ const width = rect.width || this.canvas.width / this.opts.dpr;
133
+ const height = rect.height || this.canvas.height / this.opts.dpr;
134
+
135
+ // Background
136
+ ctx.clearRect(0, 0, width, height);
137
+ if (this.opts.background) {
138
+ ctx.fillStyle = this.opts.background;
139
+ ctx.fillRect(0, 0, width, height);
140
+ }
141
+
142
+ // Sphere geometry
143
+ const pad = this.opts.padding;
144
+ const r = Math.max(1, Math.min(width, height) / 2 - pad);
145
+ const cx = width / 2;
146
+ const cy = height / 2;
147
+
148
+ // Offscreen buffer in CSS pixels for clean putImageData/drawImage
149
+ // (quality allows oversampling for smoother edges/specular)
150
+ const q = this.opts.quality;
151
+ const d = Math.max(2, Math.floor(2 * r * q));
152
+ const off = this._getOffscreen(d, d);
153
+ const id = off.ctx.createImageData(d, d);
154
+ const data = id.data;
155
+
156
+ // Precompute lighting constants
157
+ const L = this.sun; // [x,y,z], unit
158
+ const V = [0, 0, 1]; // viewer along +Z
159
+ const H = this._normalize([ L[0] + V[0], L[1] + V[1], L[2] + V[2] ]); // Blinn-Phong half-vector
160
+
161
+ const base = this.opts.baseColor; // base ocean color (linear-ish 0..1)
162
+ const amb = this.opts.ambient;
163
+ const kd = this.opts.diffuse;
164
+ const ks = this.opts.specular;
165
+ const shin = this.opts.shininess;
166
+ const termSoft = this.opts.terminatorSoftness;
167
+ const atm = this.opts.atmosphere;
168
+ const back = this.opts.backlight;
169
+
170
+ // Helpers
171
+ const clamp01 = (x) => Math.max(0, Math.min(1, x));
172
+ const smoothstep = (e0, e1, x) => {
173
+ const t = clamp01((x - e0) / (e1 - e0));
174
+ return t * t * (3 - 2 * t);
175
+ };
176
+ const pow = Math.pow;
177
+
178
+ // Loop over the square and shade only pixels within the circle
179
+ const rad = d * 0.5;
180
+ const invR = 1 / rad;
181
+
182
+ let p = 0;
183
+ for (let y = 0; y < d; y++) {
184
+ const vy_screen = (y + 0.5 - rad) * invR; // +down in screen
185
+ for (let x = 0; x < d; x++) {
186
+ const vx = (x + 0.5 - rad) * invR; // +right
187
+ const vy = -vy_screen; // convert to +up for math
188
+
189
+ const rr = vx * vx + vy * vy;
190
+ if (rr > 1.0005) {
191
+ // outside sphere — transparent
192
+ data[p++] = 0; data[p++] = 0; data[p++] = 0; data[p++] = 0;
193
+ continue;
194
+ }
195
+
196
+ // Anti-aliased edge alpha: blend over ~1 pixel at the rim
197
+ const dist = Math.sqrt(rr);
198
+ const aa = clamp01((1 - dist) * rad); // ~1 pixel smoothing
199
+ const alpha = Math.round(255 * aa);
200
+
201
+ // Sphere normal
202
+ const vz = Math.sqrt(Math.max(0, 1 - rr));
203
+ const N = [vx, vy, vz];
204
+
205
+ // Lighting terms
206
+ const NL = N[0] * L[0] + N[1] * L[1] + N[2] * L[2]; // cosine to light
207
+ const NV = N[2]; // dot(N, V) since V = [0,0,1]
208
+ const NH = N[0] * H[0] + N[1] * H[1] + N[2] * H[2];
209
+
210
+ // Softened terminator so night/day edge isn’t razor-sharp
211
+ const dayMask = smoothstep(-termSoft, termSoft, NL) * clamp01(NL);
212
+
213
+ // Diffuse (Lambert)
214
+ const diff = kd * dayMask;
215
+
216
+ // Specular (Blinn-Phong) — only on lit side
217
+ const spec = NL > 0 ? ks * pow(clamp01(NH), shin) : 0;
218
+
219
+ // Atmospheric rim (day side): stronger near limb (low NV)
220
+ let rimDay = atm * pow(1 - clamp01(NV), 2.4) * clamp01(NL + 0.15);
221
+
222
+ // Backlit rim (sun behind Earth): glow near limb where N faces away from sun
223
+ let rimBack = 0;
224
+ if (L[2] < 0) { // sun somewhere behind viewer
225
+ const away = clamp01(-NL); // how much the normal faces away from light
226
+ rimBack = back * pow(1 - clamp01(NV), 3.2) * away;
227
+ }
228
+
229
+ // Combine lighting
230
+ let rLin = base[0] * (amb + diff) + spec + 0.40 * rimDay + 0.25 * rimBack;
231
+ let gLin = base[1] * (amb + diff) + spec + 0.65 * rimDay + 0.40 * rimBack;
232
+ let bLin = base[2] * (amb + diff) + spec + 1.00 * rimDay + 0.80 * rimBack;
233
+
234
+ // Simple tone & gamma to keep it punchy but not clipped
235
+ const tone = (c) => 1 - Math.exp(-2.6 * c); // filmic-ish
236
+ rLin = tone(rLin); gLin = tone(gLin); bLin = tone(bLin);
237
+
238
+ const r8 = Math.round(clamp01(pow(rLin, 1 / 2.2)) * 255);
239
+ const g8 = Math.round(clamp01(pow(gLin, 1 / 2.2)) * 255);
240
+ const b8 = Math.round(clamp01(pow(bLin, 1 / 2.2)) * 255);
241
+
242
+ data[p++] = r8; data[p++] = g8; data[p++] = b8; data[p++] = alpha;
243
+ }
244
+ }
245
+
246
+ // Blit with masking onto main canvas
247
+ off.ctx.putImageData(id, 0, 0);
248
+
249
+ // Draw at the correct size and position; offscreen is in CSS px already
250
+ const drawSize = (d / q);
251
+ const drawX = cx - drawSize * 0.5;
252
+ const drawY = cy - drawSize * 0.5;
253
+
254
+ // Ensure we stay within a circular footprint on the main canvas
255
+ ctx.save();
256
+ ctx.beginPath();
257
+ ctx.arc(cx, cy, r, 0, Math.PI * 2);
258
+ ctx.clip();
259
+ ctx.drawImage(off.canvas, drawX, drawY, drawSize, drawSize);
260
+ ctx.restore();
261
+
262
+ // Optional subtle outline for crispness
263
+ ctx.beginPath();
264
+ ctx.arc(cx, cy, r, 0, Math.PI * 2);
265
+ ctx.strokeStyle = "rgba(0,0,0,0.14)";
266
+ ctx.lineWidth = 1.2;
267
+ ctx.stroke();
268
+ }
269
+
270
+ // ---- Internals ------------------------------------------------------------
271
+
272
+ _getOffscreen(w, h) {
273
+ if (!this._off || this._off.canvas.width !== w || this._off.canvas.height !== h) {
274
+ const c = document.createElement("canvas");
275
+ c.width = w; c.height = h;
276
+ this._off = { canvas: c, ctx: c.getContext("2d") };
277
+ }
278
+ return this._off;
279
+ }
280
+
281
+ _normalize(v) {
282
+ const n = Math.hypot(v[0], v[1], v[2]) || 1;
283
+ return [ v[0] / n, v[1] / n, v[2] / n ];
284
+ }
285
+
286
+ // Very rough solar position in an Earth-centered, equatorial-like frame.
287
+ // Enough to get a plausible sun direction that varies with date.
288
+ _approxSunEciUnit(dateUtc) {
289
+ // Julian centuries since J2000
290
+ const JD = (dateUtc.getTime() / 86400000) + 2440587.5;
291
+ const n = JD - 2451545.0;
292
+
293
+ // mean anomaly (deg) & mean longitude (deg)
294
+ const g = this._deg2rad((357.529 + 0.98560028 * n) % 360);
295
+ const L = this._deg2rad((280.459 + 0.98564736 * n) % 360);
296
+
297
+ // ecliptic longitude (deg)
298
+ const lambda = L + this._deg2rad(1.915) * Math.sin(g) + this._deg2rad(0.020) * Math.sin(2 * g);
299
+
300
+ // obliquity of the ecliptic (deg)
301
+ const eps = this._deg2rad(23.439 - 0.00000036 * n);
302
+
303
+ // convert to equatorial (RA/Dec) then to Cartesian unit vector
304
+ const x = Math.cos(lambda);
305
+ const y = Math.cos(eps) * Math.sin(lambda);
306
+ const z = Math.sin(eps) * Math.sin(lambda);
307
+
308
+ // This vector points from Earth to Sun. Align camera so +Z ≈ viewer.
309
+ // You may rotate this to match your camera.
310
+ return this._normalize([ x, z, y ]); // a simple axis shuffle to fit our (+X right, +Y up, +Z toward viewer) convention
311
+ }
312
+
313
+ _deg2rad(d) { return d * Math.PI / 180; }
314
+ }
315
+
316
+
317
+ class Demo_UI extends Active_HTML_Document {
318
+ constructor(spec = {}) {
319
+ spec.__type_name = spec.__type_name || 'demo_ui';
320
+ super(spec);
321
+ const {context} = this;
322
+ if (typeof this.body.add_class === 'function') {
323
+ this.body.add_class('demo-ui');
324
+ }
325
+ const compose = () => {
326
+ const window = new controls.Window({
327
+ context: context,
328
+ title: 'jsgui3-html Dropdown_Menu',
329
+ pos: [5, 5]
330
+ });
331
+ window.size = [480, 400];
332
+ const canvas = new controls.canvas({
333
+ context
334
+ });
335
+ canvas.dom.attributes.id = 'globeCanvas'
336
+ canvas.size = [300, 300];
337
+ window.inner.add(canvas);
338
+ this.body.add(window);
339
+ this._ctrl_fields = this._ctrl_fields || {};
340
+ this._ctrl_fields.canvas = this.canvas = canvas;
341
+ }
342
+ if (!spec.el) {
343
+ compose();
344
+ //this.add_change_listeners();
345
+ }
346
+ }
347
+ /*
348
+ add_change_listeners() {
349
+ const {select_options} = this;
350
+ select_options.data.model.on('change', e => {
351
+ console.log('select_options.data.model change e', e);
352
+ });
353
+ }
354
+ */
355
+ activate() {
356
+ if (!this.__active) {
357
+ super.activate();
358
+ const {context, ti1, ti2} = this;
359
+ //this.add_change_listeners();
360
+ console.log('activate Demo_UI');
361
+ context.on('window-resize', e_resize => {
362
+ });
363
+
364
+ const earth = new EarthLikeSphereRenderer("globeCanvas", {
365
+ background: "#081019",
366
+ quality: 1.25, // 1.0..2.0 (higher = smoother, slower)
367
+ shininess: 140, // tighten/loosen the ocean glint
368
+ atmosphere: 0.5, // rim intensity (day side)
369
+ backlight: 0.25 // twilight rim when sun is behind Earth
370
+ });
371
+
372
+ // Place sun front-left-up:
373
+ earth.setSunFromSpherical(-35, 25);
374
+ }
375
+ }
376
+ }
377
+ Demo_UI.css = `
378
+ * {
379
+ margin: 0;
380
+ padding: 0;
381
+ }
382
+ body {
383
+ overflow-x: hidden;
384
+ overflow-y: hidden;
385
+ background-color: #E0E0E0;
386
+ }
387
+ .demo-ui {
388
+ }
389
+ `;
390
+ controls.Demo_UI = Demo_UI;
391
+ module.exports = jsgui;
@@ -0,0 +1,21 @@
1
+ const jsgui = require('./client');
2
+ const {Demo_UI} = jsgui.controls;
3
+ const Server = require('../../../server');
4
+ if (require.main === module) {
5
+ const server = new Server({
6
+ Ctrl: Demo_UI,
7
+ debug: true,
8
+ 'src_path_client_js': require.resolve('./client.js'),
9
+ });
10
+ console.log('waiting for server ready event');
11
+ server.one('ready', () => {
12
+ console.log('server ready');
13
+ server.start(52000, function (err, cb_start) {
14
+ if (err) {
15
+ throw err;
16
+ } else {
17
+ console.log('server started');
18
+ }
19
+ });
20
+ })
21
+ }