Skip to content

Commit e0b9e08

Browse files
committed
AI hacking on webGL Monitor (fix aspect ratio), pioarduino 55.03.34
1 parent 7a45e64 commit e0b9e08

File tree

5 files changed

+12553
-12481
lines changed

5 files changed

+12553
-12481
lines changed
Lines changed: 64 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
<script lang="ts">
22
import { onMount, onDestroy } from 'svelte';
3-
import {clearColors, colors, vertices, createScene, updateScene } from './monitor';
3+
import {
4+
clearColors,
5+
colors,
6+
vertices,
7+
createScene,
8+
updateScene,
9+
setMatrixDimensions
10+
} from './monitor';
411
import SettingsCard from '$lib/components/SettingsCard.svelte';
512
import { socket } from '$lib/stores/socket';
613
import ControlIcon from '~icons/tabler/adjustments';
714
8-
let el:HTMLCanvasElement;
15+
let el: HTMLCanvasElement;
916
1017
let width = -1;
1118
let height = -1;
@@ -16,103 +23,109 @@
1623
//ask the server to run the mapping, the resulting positions are sent by websocket monitor
1724
const requestLayout = async () => {
1825
// try {
19-
const response = await fetch('/rest/monitorLayout', {
20-
method: 'GET',
21-
headers: {
22-
'Content-Type': 'application/json'
23-
}
24-
});
25-
console.log("requestLayout submitted");
26-
const arrayBuffer = await response.json();
27-
console.log("requestLayout received", arrayBuffer);
26+
const response = await fetch('/rest/monitorLayout', {
27+
method: 'GET',
28+
headers: {
29+
'Content-Type': 'application/json'
30+
}
31+
});
32+
console.log('requestLayout submitted');
33+
const arrayBuffer = await response.json();
34+
console.log('requestLayout received', arrayBuffer);
2835
// } catch (error) {
2936
// console.error('Error:', error);
3037
// }
31-
}
38+
};
3239
3340
const handleMonitor = (data: Uint8Array) => {
34-
35-
if (data.length == 37) //see ModuleLightsControl.h:243
36-
handleHeader(data)
41+
if (data.length == 37)
42+
//see ModuleLightsControl.h:243
43+
handleHeader(data);
3744
else {
3845
if (isPositions) {
39-
handlePositions(data)
46+
handlePositions(data);
4047
isPositions = false;
41-
}
42-
else
43-
handleChannels(data);
48+
} else handleChannels(data);
4449
}
4550
};
4651
47-
let nrOfLights:number;
48-
let channelsPerLight:number;
49-
let offsetRGB:number;
52+
let nrOfLights: number;
53+
let channelsPerLight: number;
54+
let offsetRGB: number;
5055
let isPositions: boolean = false;
5156
// let offsetRed:number;
5257
// let offsetGreen:number;
5358
// let offsetBlue:number;
5459
// let offsetWhite:number;
5560
5661
const handleHeader = (header: Uint8Array) => {
57-
console.log("Monitor.handleHeader", header);
62+
console.log('Monitor.handleHeader', header);
5863
5964
let view = new DataView(header.buffer);
6065
61-
// let isPositions:number = header[6];
62-
isPositions = true;//(header[6] >> 0) & 0x3; // bits 0-1
66+
// let isPositions:number = header[6];
67+
isPositions = true; //(header[6] >> 0) & 0x3; // bits 0-1
6368
// offsetRed = (header[6] >> 2) & 0x3; // bits 2-3
6469
// offsetGreen = (header[6] >> 4) & 0x3; // bits 4-5
6570
// offsetBlue = (header[6] >> 6) & 0x3; // bits 6-7
6671
// offsetWhite = header[13];
6772
68-
nrOfLights = view.getUint16(12, true);// header[12] + 256 * header[13];
69-
channelsPerLight = view.getUint8(19);//header[19];
70-
offsetRGB = view.getUint8(20);//header[20];
73+
nrOfLights = view.getUint16(12, true); // header[12] + 256 * header[13];
74+
channelsPerLight = view.getUint8(19); //header[19];
75+
offsetRGB = view.getUint8(20); //header[20];
7176
7277
//rebuild scene
7378
createScene(el);
7479
7580
// let ledFactor: number = 1;//header[1];
7681
// let ledSize: number = header[23];
77-
width = view.getInt32(0, true);//header[0] + 256 * header[1];
78-
height = view.getInt32(4, true);//header[4] + 256 * header[5];;
79-
depth = view.getInt32(8, true);//header[8] + 256 * header[9];;
82+
width = view.getInt32(0, true); //header[0] + 256 * header[1];
83+
height = view.getInt32(4, true); //header[4] + 256 * header[5];;
84+
depth = view.getInt32(8, true); //header[8] + 256 * header[9];;
8085
81-
// let nrOfLights = header[4] + 256 * header[5];
86+
setMatrixDimensions(width, height);
8287
83-
console.log("Monitor.handleHeader", width, height, depth, nrOfLights, channelsPerLight, offsetRGB);
88+
// let nrOfLights = header[4] + 256 * header[5];
8489
85-
}
90+
console.log(
91+
'Monitor.handleHeader',
92+
width,
93+
height,
94+
depth,
95+
nrOfLights,
96+
channelsPerLight,
97+
offsetRGB
98+
);
99+
};
86100
87101
const handlePositions = (positions: Uint8Array) => {
88-
console.log("Monitor.handlePositions", positions);
89-
90-
for (let index = 0; index < nrOfLights * 3; index +=3) {
102+
console.log('Monitor.handlePositions', positions);
91103
104+
for (let index = 0; index < nrOfLights * 3; index += 3) {
92105
let x = positions[index];
93-
let y = positions[index+1];
94-
let z = positions[index+2];
106+
let y = positions[index + 1];
107+
let z = positions[index + 2];
95108
96109
//set to -1,1 coordinate system of webGL
97110
//width -1 etc as 0,0 should be top left, not bottom right
98-
x = width==1?0:(((x) / (width - 1)) * 2.0 - 1.0);
99-
y = height==1?0:(((height-1-y) / (height - 1)) * 2.0 - 1.0);
100-
z = depth==1?0:(((depth-1-z) / (depth - 1)) * 2.0 - 1.0);
111+
x = width == 1 ? 0 : (x / (width - 1)) * 2.0 - 1.0;
112+
y = height == 1 ? 0 : ((height - 1 - y) / (height - 1)) * 2.0 - 1.0;
113+
z = depth == 1 ? 0 : ((depth - 1 - z) / (depth - 1)) * 2.0 - 1.0;
101114
102115
vertices.push(x, y, z);
103116
}
104-
}
117+
};
105118
106119
const handleChannels = (channels: Uint8Array) => {
107-
108120
if (!done) {
109121
requestLayout(); //ask for positions
110-
console.log("Monitor.handleChannels", channels);
122+
console.log('Monitor.handleChannels', channels);
111123
done = true;
112124
}
113125
clearColors();
114126
//max size supported is 255x255x255 (index < width * height * depth) ... todo: only any of the component < 255
115-
for (let index = 0; index < nrOfLights * channelsPerLight; index += channelsPerLight) { // && index < width * height * depth
127+
for (let index = 0; index < nrOfLights * channelsPerLight; index += channelsPerLight) {
128+
// && index < width * height * depth
116129
// colorLed(index/3, data[index]/255, data[index+1]/255, data[index+2]/255);
117130
const r = channels[index + offsetRGB + 0] / 255;
118131
const g = channels[index + offsetRGB + 1] / 255;
@@ -125,15 +138,14 @@
125138
};
126139
127140
onMount(() => {
128-
console.log("onMount Monitor")
129-
socket.on("monitor", handleMonitor);
141+
console.log('onMount Monitor');
142+
socket.on('monitor', handleMonitor);
130143
});
131144
132145
onDestroy(() => {
133-
console.log("onDestroy Monitor");
134-
socket.off("monitor", handleMonitor);
146+
console.log('onDestroy Monitor');
147+
socket.off('monitor', handleMonitor);
135148
});
136-
137149
</script>
138150

139151
<SettingsCard collapsible={false}>
@@ -147,5 +159,4 @@
147159
<div class="w-full overflow-x-auto">
148160
<canvas bind:this={el} width="720" height="360"></canvas>
149161
</div>
150-
151-
</SettingsCard>
162+
</SettingsCard>

interface/src/routes/moonbase/monitor/monitor.ts

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,20 @@
33

44
import { mat4 } from 'gl-matrix';
55

6-
let uMVPLocation: WebGLUniformLocation;
6+
let uMVPLocation: WebGLUniformLocation | null = null;
77

8-
let gl: WebGLRenderingContext;
9-
let program: WebGLProgram;
10-
let positionBuffer: WebGLBuffer;
8+
let gl: WebGLRenderingContext | null = null;
9+
let program: WebGLProgram | null = null;
10+
let positionBuffer: WebGLBuffer | null = null;
1111

1212
export let vertices: number[] = [];
1313
export let colors: number[] = [];
1414

15-
let colorBuffer: WebGLBuffer; // Buffer for color data
15+
// Store LED matrix dimensions
16+
let matrixWidth: number = 1;
17+
let matrixHeight: number = 1;
18+
19+
let colorBuffer: WebGLBuffer | null = null; // Buffer for color data
1620

1721
export function createScene(el: HTMLCanvasElement) {
1822
// Initialize WebGL
@@ -77,14 +81,15 @@ export function createScene(el: HTMLCanvasElement) {
7781

7882
const createShader = (gl: WebGLRenderingContext, type: number, source: string): WebGLShader => {
7983
const shader = gl.createShader(type);
80-
if (shader) {
81-
gl.shaderSource(shader, source);
82-
gl.compileShader(shader);
83-
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
84-
console.error(gl.getShaderInfoLog(shader));
85-
gl.deleteShader(shader);
86-
throw new Error("Shader compilation failed");
87-
}
84+
if (!shader) {
85+
throw new Error("Unable to create shader");
86+
}
87+
gl.shaderSource(shader, source);
88+
gl.compileShader(shader);
89+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
90+
console.error(gl.getShaderInfoLog(shader));
91+
gl.deleteShader(shader);
92+
throw new Error("Shader compilation failed");
8893
}
8994
return shader;
9095
};
@@ -110,12 +115,20 @@ export function clearVertices() {
110115
vertices = [];
111116
}
112117

118+
// New function to set the LED matrix dimensions
119+
export function setMatrixDimensions(width: number, height: number) {
120+
matrixWidth = width;
121+
matrixHeight = height;
122+
}
123+
113124
export const updateScene = (vertices: number[], colors: number[]) => {
114-
if (!gl) return;
125+
if (!gl || !positionBuffer || !colorBuffer) return;
115126

116127
// Set the MVP matrix
117128
const mvp = getMVPMatrix();
118-
gl.uniformMatrix4fv(uMVPLocation, false, mvp);
129+
if (uMVPLocation) {
130+
gl.uniformMatrix4fv(uMVPLocation, false, mvp);
131+
}
119132

120133
// Bind the position buffer and upload the vertex data
121134
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
@@ -132,19 +145,48 @@ export const updateScene = (vertices: number[], colors: number[]) => {
132145
gl.drawArrays(gl.POINTS, 0, vertices.length / 3);
133146
};
134147

135-
function getMVPMatrix(): Float32Array {
136-
const aspect = gl.canvas.width / gl.canvas.height;
137-
const fov = Math.PI / 6; // from 4 (45 degrees) to 6
148+
function getMVPMatrix(): mat4 {
149+
const canvas = gl!.canvas as HTMLCanvasElement;
150+
const canvasAspect = canvas.width / canvas.height;
151+
const fov = Math.PI / 6; // 30 degrees
138152
const near = 0.1;
139153
const far = 100.0;
140154

141155
const projection = mat4.create();
142-
mat4.perspective(projection, fov, aspect, near, far);
156+
mat4.perspective(projection, fov, canvasAspect, near, far);
157+
158+
// Calculate the required camera distance to fit the matrix in view
159+
const matrixAspect = matrixWidth / matrixHeight;
160+
161+
// The object will be scaled by matrixAspect in X direction and 1 in Y direction
162+
// We need to fit the larger dimension in the view
163+
// Calculate the size of the object after scaling
164+
const objectWidth = matrixAspect;
165+
const objectHeight = 1;
166+
167+
// Determine which dimension is limiting based on canvas aspect
168+
// The vertical FOV determines how much we see vertically
169+
// The horizontal FOV is: horizontal = 2 * atan(tan(vertical/2) * aspect)
170+
const verticalSize = objectHeight;
171+
const horizontalSize = objectWidth;
172+
173+
// Calculate required distance for vertical fit
174+
const distanceForHeight = verticalSize / (2 * Math.tan(fov / 2));
175+
176+
// Calculate required distance for horizontal fit
177+
const horizontalFov = 2 * Math.atan(Math.tan(fov / 2) * canvasAspect);
178+
const distanceForWidth = horizontalSize / (2 * Math.tan(horizontalFov / 2));
179+
180+
// Use the larger distance to ensure both dimensions fit
181+
const cameraDistance = Math.max(distanceForHeight, distanceForWidth) * 2.5; // 1.2 adds some padding
143182

144183
const view = mat4.create();
145-
mat4.lookAt(view, [0, 0, 5], [0, 0, 0], [0, 1, 0]); // Camera at (0,0,5) looking at origin
184+
mat4.lookAt(view, [0, 0, cameraDistance], [0, 0, 0], [0, 1, 0]);
146185

147-
const model = mat4.create(); // Identity
186+
const model = mat4.create();
187+
188+
// Scale by the matrix aspect ratio to get correct proportions
189+
mat4.scale(model, model, [matrixAspect, 1, 1]);
148190

149191
const mvp = mat4.create();
150192
mat4.multiply(mvp, projection, view);

0 commit comments

Comments
 (0)