I’m thinking of learning it
I always wondered. Are there any contemporary famous programmers who struggled to understand coding concepts, when they where at the beginning of their learning journey?
cross-posted from: https://lemm.ee/post/46067136
> I'm designing a webapp that is supposed to be an AR environment on your phone, to be viewed with something like Google Cardboard, but I am having an issue that the segmentPointer object that is meant to appear when clicking on an object is not.
>
> I've checked the geometry displays correctly in a sandbox, and when I change it to a 3d object rather than shapeGeometry it does display, but I cannot figure out why it is not displaying how I want it to.
>
> The project is at [https://voxelverse.jackgreenearth.org](https://voxelverse.jackgreenearth.org), and the code is quite long, but it is here to read in its totality below as it might need the whole context to discover the error. I've tried myself looking through the code, and I've tried searching the web and asking LLMs, but I couldn't figure it out, so please help me, fellow humans.
>
> ::: spoiler Tap for code
>
> ```
> "use strict";
>
> import \* as THREE from 'three';
>
> import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';
>
> \
>
>
> const loader = new GLTFLoader();
>
> const textureLoader = new THREE.TextureLoader();
>
> const manager = THREE.DefaultLoadingManager;
>
> \
>
>
> // Basic functions
>
> \
>
>
> function ls(id) {
>
> return(localStorage.getItem(id));
>
> };
>
> \
>
>
> function setLs(id, val) {
>
> localStorage.setItem(id, val);
>
> };
>
> \
>
>
> function byId(id) {
>
> return(document.getElementById(id));
>
> };
>
> \
>
>
> function bySel(sel) {
>
> return(document.querySelector(sel));
>
> };
>
> \
>
>
> function byClass(id) {
>
> return(document.getElementsByClassName(id));
>
> };
>
> \
>
>
> function toTitleCase(str) {
>
> return str.replace(
>
> /\w\S\*/g,
>
> function(txt) {
>
> return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
>
> }
>
> );
>
> };
>
> \
>
>
> function randInt(max) {
>
> return Math.floor(Math.random() \* (max));
>
> };
>
> \
>
>
> function getRandomFloat(min, max, decimals) {
>
> return(parseFloat((Math.random() \* (max - min) + min).toFixed(decimals)));
>
> };
>
> \
>
>
> function confine(value, min, max) {
>
> if(value < min) {
>
> return(min);
>
> } else if(value > max) {
>
> return(max);
>
> } else {
>
> return(value);
>
> };
>
> };
>
> \
>
>
> function wrap(value, min, max) {
>
> const range = max - min;
>
> \
>
>
> if(value < min) {
>
> return(wrap(value + range, min, max));
>
> } else if(value > max) {
>
> return(wrap(value - range, min, max));
>
> } else {
>
> return(value);
>
> };
>
> };
>
> \
>
>
> function removeFromArray(array, forDeletion) {
>
> return(array.filter(item => !forDeletion.includes(item)));
>
> };
>
> \
>
>
> function radToDeg(radians) {
>
> return radians \* (180 / PI);
>
> }
>
> \
>
>
> function range(start, stop, step = 1) {
>
> if (stop === undefined) {
>
> stop = start;
>
> start = 0
>
> }
>
> return Array.from({ length: (stop - start) / step }, (\_, i) => start + (i \* step));
>
> }
>
> \
>
>
> function between(variable, min, max, inclusive='min') {
>
> switch(inclusive) {
>
> case 'none':
>
> return((variable > min) && (variable < max));
>
> break;
>
> case 'both':
>
> return((variable >= min) && (variable <= max));
>
> break;
>
> case 'min':
>
> return((variable >= min) && (variable < max));
>
> break;
>
> case 'max':
>
> return((variable > min) && (variable <= max));
>
> break;
>
> }
>
> }
>
> \
>
>
> function download(data, filename, type) {
>
> var file = new Blob(\[data], {type: type});
>
> if (window\.navigator.msSaveOrOpenBlob) // IE10+
>
> window\.navigator.msSaveOrOpenBlob(file, filename);
>
> else { // Others
>
> var a = document.createElement("a"),
>
> url = URL.createObjectURL(file);
>
> a.href = url;
>
> a.download = filename;
>
> document.body.appendChild(a);
>
> a.click();
>
> setTimeout(function() {
>
> document.body.removeChild(a);
>
> window\.URL.revokeObjectURL(url);
>
> }, 0);
>
> };
>
> };
>
> \
>
>
> function log(text) {
>
> console.log(text);
>
> };
>
> \
>
>
> function distance2d(x1, y1, x2, y2) {
>
> return(Math.sqrt(
>
> (Math.abs(x1 - x2) \*\* 2) +
>
> (Math.abs(y1 - y2) \*\* 2)
>
> ));
>
> };
>
> \
>
>
> function distance3d(p1 = new THREE.Vector3(0, 0, 0), p2 = new THREE.Vector3(0, 0, 0)) {
>
> return(Math.sqrt((distance2d(p1.x, p1.y, p2.x, p2.y) \*\* 2) + (Math.abs(p1.z - p2.z) \*\* 2)));
>
> };
>
> \
>
>
> let totalElementsToLoad = 0;
>
> let numberOfElementsLoaded = 0;
>
> \
>
>
> function onAllElementsLoaded() {
>
> \
>
>
> }
>
> \
>
>
> function load(path, type, functionOnLoad) {
>
> totalElementsToLoad += 1;
>
> \
>
>
> if(type == 'html') {
>
> fetch(path)
>
> .then(response => response.text())
>
> .then(html => {
>
> let doc = new DOMParser().parseFromString(html, "text/html");
>
> \
>
>
> functionOnLoad(doc);
>
> \
>
>
> // If all elements to load have been loaded, execute the relevant function
>
> numberOfElementsLoaded += 1;
>
> if(numberOfElementsLoaded == totalElementsToLoad) {
>
> onAllElementsLoaded();
>
> }
>
> })
>
> .catch(error => {
>
> console.error(error);
>
> });
>
> } else if(type == 'json') {
>
> fetch(path)
>
> .then(response => response.json()) // parse the response as JSON
>
> .then(json => {
>
> functionOnLoad(json);
>
> \
>
>
> // If all elements to load have been loaded, execute the relevant function
>
> numberOfElementsLoaded += 1;
>
> if(numberOfElementsLoaded == totalElementsToLoad) {
>
> onAllElementsLoaded();
>
> }
>
> })
>
> .catch(error => {
>
> console.error(error);
>
> });
>
> }
>
> }
>
> \
>
>
> // Setup
>
> \
>
>
> const PI = 3.1415926535897932384626433832795028841971;
>
> \
>
>
> // Objects
>
> \
>
>
> let orientation = {
>
> 'absolute': false,
>
> 'alpha': 0,
>
> 'beta': 0,
>
> 'gamma': 0
>
> }
>
> \
>
>
> // vars
>
> const fps = 60;
>
> \
>
>
> let keysDown = \[];
>
> let pointerPosition = {'x': 0, 'y': 0, 'positions': \[{'clientX': 0, 'clientY': 0}], 'type': 'mouse'};
>
> \
>
>
> // Camera
>
> let cameraRotation = new THREE.Euler(0, 0, 0, 'YXZ');
>
> let cameraTargetRotation = {'x': 0, 'y': 0, 'z': 0};
>
> const cameraRotationSensitivity = 0.002;
>
> \
>
>
> // Other variables
>
> let logicInterval;
>
> \
>
>
> // Load default settings
>
> let defaultSettings;
>
> \
>
>
> load("/assets/json/default-settings.json", 'json', function(defset) {
>
> defaultSettings = defset;
>
> \
>
>
> // Create custom settings
>
> if(!Object.keys(localStorage).includes('settings')) {
>
> setLs('settings', JSON.stringify({}));
>
> };
>
> \
>
>
> onSettingsLoad();
>
> });
>
> \
>
>
> function settingURL(url, addValue=true) {
>
> return('children/' + url.split('/').join('/children/') + (addValue ? '/value' : ''));
>
> }
>
> \
>
>
> function customiseSetting(url, value) {
>
> url = settingURL(url).split('/');
>
> \
>
>
> let newSettings;
>
> \
>
>
> function recursiveSet(object, list, index, setTo) {
>
> // If the current component is the last one, assign the value
>
> if(index == list.length - 1) {
>
> object\[list\[index]] = setTo;
>
> return(object);
>
> } else {
>
> // Check if it already contains the value
>
> if(object.hasOwnProperty(list\[index])) {
>
> object\[list\[index]] = recursiveSet(object\[list\[index]], list, index + 1, setTo);
>
> } else {
>
> object\[list\[index]] = recursiveSet({}, list, index + 1, setTo);
>
> }
>
> return(object);
>
> }
>
> };
>
> \
>
>
> newSettings = recursiveSet(JSON.parse(ls('settings')), url, 0, value);
>
> \
>
>
> setLs('settings', JSON.stringify(newSettings));
>
> }
>
> \
>
>
> function getSetting(url, addValue) {
>
> url = settingURL(url, addValue).split('/');
>
> \
>
>
> function recursiveGet(object, list, index) {
>
> // If the current component is the last one, return the value
>
> if (index == list.length - 1) {
>
> return object\[list\[index]];
>
> } else {
>
> // Check if it contains the value
>
> if (object.hasOwnProperty(list\[index])) {
>
> return recursiveGet(object\[list\[index]], list, index + 1);
>
> } else {
>
> return null; // No such setting
>
> }
>
> }
>
> }
>
> \
>
>
> // Try to find it in local settings first, otherwise get it from defaultSettings
>
> const localGet = recursiveGet(JSON.parse(ls('settings')), url, 0);
>
> if(localGet == null) {
>
> return(recursiveGet(defaultSettings, url, 0));
>
> } else {
>
> return(localGet);
>
> }
>
> }
>
> \
>
>
> // First, lets define some functions
>
> // Rendering functions
>
> \
>
>
> // Thanks, https\://discourse.threejs.org/t/roundedrectangle-squircle/28645!
>
> function roundRectangleGeometry(w, h, r, s) { // width, height, radius corner, smoothness
>
> // helper const's
>
> const wi = w / 2 - r; // inner width
>
> const hi = h / 2 - r; // inner height
>
> const w2 = w / 2; // half width
>
> const h2 = h / 2; // half height
>
> const ul = r / w; // u left
>
> const ur = ( w - r ) / w; // u right
>
> const vl = r / h; // v low
>
> const vh = ( h - r ) / h; // v high
>
> let positions = \[
>
> -wi, -h2, 0, wi, -h2, 0, wi, h2, 0,
>
> -wi, -h2, 0, wi, h2, 0, -wi, h2, 0,
>
> -w2, -hi, 0, -wi, -hi, 0, -wi, hi, 0,
>
> -w2, -hi, 0, -wi, hi, 0, -w2, hi, 0,
>
> wi, -hi, 0, w2, -hi, 0, w2, hi, 0,
>
> wi, -hi, 0, w2, hi, 0, wi, hi, 0
>
> ];
>
> let uvs = \[
>
> ul, 0, ur, 0, ur, 1,
>
> ul, 0, ur, 1, ul, 1,
>
> 0, vl, ul, vl, ul, vh,
>
> 0, vl, ul, vh, 0, vh,
>
> ur, vl, 1, vl, 1, vh,
>
> ur, vl, 1, vh, ur, vh
>
> ];
>
> let phia = 0;
>
> let phib, xc, yc, uc, vc, cosa, sina, cosb, sinb;
>
> for (let i = 0; i < s \* 4; i ++) {
>
> phib = Math.PI \* 2 \* ( i + 1 ) / ( 4 \* s );
>
> cosa = Math.cos( phia );
>
> sina = Math.sin( phia );
>
> cosb = Math.cos( phib );
>
> sinb = Math.sin( phib );
>
> xc = i < s || i >= 3 \* s ? wi : - wi;
>
> yc = i < 2 \* s ? hi : -hi;
>
> positions.push( xc, yc, 0, xc + r \* cosa, yc + r \* sina, 0, xc + r \* cosb, yc + r \* sinb, 0 );
>
> uc = i < s || i >= 3 \* s ? ur : ul;
>
> vc = i < 2 \* s ? vh : vl;
>
> uvs.push( uc, vc, uc + ul \* cosa, vc + vl \* sina, uc + ul \* cosb, vc + vl \* sinb );
>
> phia = phib;
>
> }
>
> const geometry = new THREE.BufferGeometry( );
>
> geometry.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( positions ), 3 ) );
>
> geometry.setAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( uvs ), 2 ) );
>
> return geometry;
>
> }
>
> \
>
>
> // Render
>
> function render() {
>
> requestAnimationFrame(render);
>
> leftRenderer.render(scene, leftCamera);
>
> rightRenderer.render(scene, rightCamera);
>
> \
>
>
> framesSoFar++;
>
> };
>
> \
>
>
> // Functions
>
> function setCameraRotation() {
>
> // Calculate drag
>
> cameraRotation.x = Number((cameraRotation.x + ((cameraTargetRotation.x - cameraRotation.x) / getSetting('Input/Mouse/Camera Rotation Drag'))).toFixed(5));
>
> cameraRotation.y = Number((cameraRotation.y + ((cameraTargetRotation.y - cameraRotation.y) / getSetting('Input/Mouse/Camera Rotation Drag'))).toFixed(5));
>
> cameraRotation.z = Number((cameraRotation.z + ((cameraTargetRotation.z - cameraRotation.z) / getSetting('Input/Mouse/Camera Rotation Drag'))).toFixed(5));
>
> // Update cameras
>
> for(let camera of \[leftCamera, rightCamera]) {
>
> camera.rotation.set(cameraRotation.x, cameraRotation.y, cameraRotation.z, 'YXZ');
>
> }
>
> \
>
>
> const eyeGap = getSetting('Quick Settings/Eye Gap');
>
> \
>
>
> // Set camera positions
>
> leftCamera.position.x = -1 \* eyeGap \* Math.sin(cameraRotation.y);
>
> leftCamera.position.z = -1 \* eyeGap \* Math.cos(cameraRotation.y);
>
> rightCamera.position.x = eyeGap \* Math.sin(cameraRotation.y);
>
> rightCamera.position.z = eyeGap \* Math.cos(cameraRotation.y);
>
> \
>
>
> byId('camera-target-rot-x').innerHTML = cameraTargetRotation.x.toFixed(2);
>
> byId('camera-target-rot-y').innerHTML = cameraTargetRotation.y.toFixed(2);
>
> byId('camera-target-rot-z').innerHTML = cameraTargetRotation.z.toFixed(2);
>
> byId('camera-rot-x').innerHTML = cameraRotation.x.toFixed(2);
>
> byId('camera-rot-y').innerHTML = cameraRotation.y.toFixed(2);
>
> byId('camera-rot-z').innerHTML = cameraRotation.z.toFixed(2);
>
> \
>
>
> byId('camera-left-rot-x').innerHTML = leftCamera.rotation.x.toFixed(2);
>
> byId('camera-left-rot-y').innerHTML = leftCamera.rotation.y.toFixed(2);
>
> byId('camera-left-rot-z').innerHTML = leftCamera.rotation.z.toFixed(2);
>
> }
>
> \
>
>
> function takeScreenshot() {
>
> downloadCanvasImage(document.getElementById('game-canvas'), gameName + ' screenshot');
>
> sendAlert('Screenshot Taken!', 'tick');
>
> };
>
> \
>
>
> function takePanorama() {
>
> const canvas = document.getElementById('game-canvas');
>
> const height = canvas.height;
>
> const width = canvas.width \* (360 / (camera.fov \* camera.aspect));
>
> let newCanvas = document.createElement('canvas');
>
> newCanvas.height = height;
>
> newCanvas.width = width;
>
> newCanvas.style.display = 'none';
>
> let context = newCanvas.getContext("2d");
>
> document.body.appendChild(newCanvas);
>
> for(let x = 0; x < width; x++) {
>
> // Rotate
>
> cameraRotation.y += ((2 \* PI) / width);
>
> let calculatedRotation = rotationToAbsolute(playerPosition, cameraRotation);
>
> camera.rotation.set(calculatedRotation.x, calculatedRotation.y, calculatedRotation.z, 'YXZ');
>
> renderer.render(scene, camera);
>
> const gl = renderer.getContext();
>
> // Get canvas data
>
> const pixelData = new Uint8ClampedArray(1 \* height \* 4);
>
> const reversedPixelData = new Uint8ClampedArray(1 \* height \* 4);
>
> gl.readPixels((canvas.width / 2), 0, 1, height, gl.RGBA, gl.UNSIGNED\_BYTE, pixelData);
>
> for (let i = 0; i < height; i++) {
>
> for (let j = 0; j < 4; j++) {
>
> reversedPixelData\[i\*4 + j] = pixelData\[(height - i - 1)\*4 + j];
>
> };
>
> };
>
> const imageData = new ImageData(reversedPixelData, 1, height);
>
> context.putImageData(imageData, x, 0);
>
> };
>
> downloadCanvasImage(newCanvas, gameName + ' panorama');
>
> newCanvas.remove();
>
> sendAlert('Panoramic screenshot taken!', 'tick');
>
> };
>
> \
>
>
> function setRotation(object, rotation) {
>
> object.rotation.set(rotation.x, rotation.y, rotation.z);
>
> };
>
> \
>
>
> function downloadCanvasImage(canvas, name) {
>
> let canvasImage = canvas.toDataURL('image/png');
>
> // this can be used to download any image from webpage to local disk
>
> let xhr = new XMLHttpRequest();
>
> xhr.responseType = 'blob';
>
> xhr.onload = function () {
>
> let a = document.createElement('a');
>
> a.href = window\.URL.createObjectURL(xhr.response);
>
> a.download = name;
>
> a.style.display = 'none';
>
> document.body.appendChild(a);
>
> a.click();
>
> a.remove();
>
> };
>
> xhr.open('GET', canvasImage); // This is to download the canvas image
>
> xhr.send();
>
> };
>
> \
>
>
> function xyToRealPosRot(x, y, distance) {
>
> let realX, realY, realZ, rotX, rotY, rotZ;
>
> \
>
>
> // Position is an object {x: x, y: y} x determines which face it will be on horizontally, and y determines if it will be on the top or the bottom
>
> // Beyond 400, x position wraps
>
> x = wrap(x, 0, 400);
>
> log('x before: ' + x)
>
> const horizontalFace = (x / 100) % 4;
>
> //rotY = (x / 400) \* (1) // horizontalFace);
>
> \
>
>
> // The top of the screen is y 100, the bottom is y -100, and the horizontals are between -50 and 50
>
> realY = confine(y, -100, 100);
>
> \
>
>
> // Calculate real position
>
> const unit = getSetting('Display/UI/Distance') / 50;
>
> \
>
>
> let forward = getSetting('Display/UI/Distance');
>
> \
>
>
> const bevel = getSetting('Display/UI/Bevel');
>
> \
>
>
> rotX = 0;
>
> \
>
>
> // If it is horizontal...
>
> if(between(y, -50 + bevel, 50 - bevel)) {
>
> realY = y;
>
> rotX = 0;
>
> } else if(y < -50 - bevel) {
>
> // If it is on the lower face
>
> realY = -50;
>
> forward = (y + 100) \* unit;
>
> rotX = -(PI / 2);
>
> } else if(y >= 50 + bevel) {
>
> // If it is on the upper face
>
> realY = 50;
>
> forward = (y - 100) \* unit;
>
> //side = unit \* (((x - 50) % 100) + 50);
>
> rotX = (PI / 2);
>
> } else if(between(y, -50 - bevel, -50 + bevel)) {
>
> // If it is on the lower bevel
>
> realY = -50 - ((y + 50) / 2);
>
> rotX = (PI / 4);
>
> } else if(between(y, 50 - bevel, 50 + bevel)) {
>
> // If it is on the upper bevel
>
> realY = 50 + ((y - 50) / 2) ;
>
> rotX = -(PI / 4);
>
> }
>
> \
>
>
> realY = realY \* unit;
>
> \
>
>
> let flip = false;
>
> \
>
>
> /\*if(
>
> (horizontalFace >= 0 && horizontalFace < 0.5) ||
>
> (horizontalFace >= 1.5 && horizontalFace < 2.5) ||
>
> (horizontalFace >= 3.5 && horizontalFace < 4)
>
> ) {
>
> flip = true;
>
> }\*/
>
> \
>
>
> let angle = (x / 400) \* (PI \* 2);
>
> realX = Math.sin(angle) \* forward;
>
> realZ = Math.cos(angle) \* forward;
>
> rotY = angle;
>
> log('rot y: ' + rotY)
>
> \
>
>
> log({
>
> 'x': realX,
>
> 'y': realY,
>
> 'forward': forward,
>
> })
>
> \
>
>
> // Take distance into account
>
> realX \*= distance;
>
> realY \*= distance;
>
> realZ \*= distance;
>
> \
>
>
> return({
>
> 'position': new THREE.Vector3(realX, realY, realZ),
>
> 'rotation': new THREE.Euler(rotX, rotY, rotZ, 'YXZ'),
>
> 'flip': flip
>
> });
>
> }
>
> \
>
>
> function addWidget({
>
> name = '',
>
> position = {'x': 0, 'y': 0},
>
> rotation = {'x': 0, 'y': 0, 'z': 0},
>
> distance = 1,
>
> size = {'x': 10, 'y': 10},
>
> radius = 3,
>
> shape = 'rRect',
>
> background = '#000000',
>
> opacity,
>
> textStyle = {
>
> 'align': 'center',
>
> 'weight': 0, // Range is 0 to 10
>
> 'font': 'DINRoundPro,arial,sans-serif',
>
> 'color': '#b0b0b0',
>
> 'vertical-align': 'center',
>
> 'font-size': 1 // Uses the same sizing system as the rest of the UI, so one unit of text is also one unit of object
>
> },
>
> textContent = '',
>
> onclick = function() {},
>
> onlongpress = function() {},
>
> onhover = function() {},
>
> onhoverexit = function() {},
>
> ontruehover = function() {}
>
> }) {
>
> const realPosRot = xyToRealPosRot(position.x, position.y, distance);
>
> log(realPosRot)
>
> const realPos = realPosRot.position;
>
> let realRot = realPosRot.rotation;
>
> \
>
>
> realRot.x += rotation.x;
>
> realRot.y += rotation.y;
>
> realRot.z = rotation.z;
>
> \
>
>
> // Calculate real size
>
> const unit = getSetting('Display/UI/Distance') / 100;
>
> \
>
>
> let width = unit \* size.x;
>
> let height = unit \* size.y;
>
> radius \*= unit;
>
> const scale = getSetting('Display/UI/Scale/General');
>
> width \*= scale;
>
> height \*= scale;
>
> radius \*= scale;
>
> \
>
>
> // Set mesh geometry
>
> let geometry;
>
> switch(shape) {
>
> case 'rRect':
>
> geometry = roundRectangleGeometry(width, height, radius, 10);
>
> break;
>
> case 'rect':
>
> geometry = new THREE.PlaneGeometry(width, height);
>
> break;
>
> case 'circle':
>
> geometry = new THREE.CircleGeometry((width + height) / 2, 32);
>
> break;
>
> }
>
> let material;
>
> \
>
>
> if(opacity == undefined) {
>
> opacity = 1;
>
> }
>
> \
>
>
> if(textContent == '') {
>
> if(background\[0] == '/') {
>
> loadTexture(background, function(texture) {
>
> material = new THREE.MeshBasicMaterial({
>
> map: texture,
>
> side: THREE.DoubleSide,
>
> opacity: opacity,
>
> transparent: true
>
> });
>
> onTextureLoad(material);
>
> })
>
> } else {
>
> material = new THREE.MeshBasicMaterial({
>
> color: background,
>
> side: THREE.DoubleSide,
>
> opacity: opacity,
>
> transparent: true
>
> });
>
> onTextureLoad(material);
>
> }
>
> } else {
>
> function prepareText(canvas) {
>
> // Proceed to prepare the canvas with the text
>
> ctx.font = \`${textStyle\["font-size"]}em ${textStyle\["font"]}\`;
>
> ctx.textAlign = textStyle\["align"];
>
> ctx.fillStyle = textStyle\["color"];
>
> ctx.fillText(textContent, 0, 0);
>
> // Compose the text onto the background
>
> const composedTexture = new THREE.CanvasTexture(canvas);
>
> \
>
>
> // Generate the material
>
> material = new THREE.MeshBasicMaterial({
>
> map: composedTexture,
>
> side: THREE.DoubleSide,
>
> transparent: true,
>
> alphaTest: 0.5
>
> });
>
> \
>
>
> onTextureLoad(material);
>
> }
>
> \
>
>
> // Initialize tmpcanvas only when needed
>
> const tmpcanvas = document.createElement('canvas');
>
> tmpcanvas.width = width;
>
> tmpcanvas.height = height;
>
> const ctx = tmpcanvas.getContext('2d');
>
> \
> \
>
>
> // Fill the background first
>
> if (background\[0] == '/') {
>
> loadTexture(background, function(texture) {
>
> ctx.fillStyle = texture;
>
> ctx.fillRect(0, 0, width, height);
>
> \
>
>
> prepareText(tmpcanvas);
>
> })
>
> } else {
>
> ctx.fillStyle = background;
>
> ctx.fillRect(0, 0, width, height);
>
> \
>
>
> prepareText(tmpcanvas);
>
> }
>
> }
>
> function onTextureLoad(material) {
>
> // Create a mesh with the geometry and the material
>
> let mesh = new THREE.Mesh(geometry, material);
>
> \
>
>
> mesh.name = name;
>
> \
>
>
> mesh.position.set(realPos.x, realPos.y, realPos.z );
>
> mesh.rotation.set(realRot.x, realRot.y, realRot.z);
>
> \
>
>
> if(realPosRot.flip) {
>
> mesh.scale.x = -1;
>
> }
>
> \
>
>
> mesh.onclick = onclick;
>
> mesh.onlongpress = onlongpress;
>
> mesh.onhoverexit = onhoverexit;
>
> mesh.ontruehover = ontruehover;
>
> mesh.onchover = onhover;
>
> \
>
>
> scene.add(mesh);
>
> };
>
> }
>
> \
>
>
> function transitionWidget(name, property, newProperty, time, condition) {
>
> if(condition != null) {
>
> }
>
> }
>
> \
>
>
> // three.js Scene setup
>
> const scene = new THREE.Scene();
>
> \
>
>
> // three functions
>
> \
>
>
> function loadTexture(path, onload) {
>
> textureLoader.load(path, function (texture) {
>
> onload(texture);
>
> }, undefined, function (error) {
>
> console.error(error);
>
> });
>
> };
>
> \
>
>
> // Define objects to make them global, they will mostly be only added to the scene when settings are loaded
>
> let sun;
>
> let wallpaper;
>
> let cameraStream;
>
> let pointer;
>
> \
>
>
> let pointerMaterial = new THREE.MeshBasicMaterial({
>
> color: "hsl(0, 100%, 50%)",
>
> side: THREE.DoubleSide
>
> });
>
> \
>
>
> let segmentShape = new THREE.Shape();
>
> let segmentGeometry = new THREE.ShapeGeometry(segmentShape);
>
> let segmentPointer = new THREE.Mesh(segmentGeometry, pointerMaterial);
>
> segmentPointer.name = 'segmentPointer';
>
> \
>
>
> function setSegmentPointer(angle = 0, radius = 0.1, rotation = new THREE.Euler(0, 0, 0), clockwise=true) {
>
> let oldGeometry = segmentPointer.geometry;
>
> \
>
>
> let segmentShape = new THREE.Shape();
>
> segmentShape.moveTo(0, 0);
>
> segmentShape.arc(0, 0, radius, 0, angle, clockwise);
>
> segmentShape.lineTo(0, 0);
>
> \
>
>
> let extrudeSettings = {
>
> steps: 1,
>
> depth: 0.1,
>
> bevelEnabled: false
>
> };
>
> \
>
>
> let segmentGeometry = new THREE.ExtrudeGeometry(segmentShape, extrudeSettings);
>
> segmentPointer.geometry = segmentGeometry;
>
> \
>
>
> oldGeometry.dispose();
>
> \
>
>
> segmentPointer.rotation.set(rotation);
>
> }
>
> \
>
>
> // Camera stuff
>
> let cameraViewDistance;
>
> \
>
>
> // Setup cameras
>
> let leftCamera;
>
> let rightCamera;
>
> let leftRenderer;
>
> let rightRenderer;
>
> \
>
>
> function setRendererSize() {
>
> for(let renderer of \[leftRenderer, rightRenderer]) {
>
> let canvas = renderer.domElement;
>
> renderer.setSize(
>
> canvas.offsetWidth \* getSetting('Display/Anti-alias'),
>
> canvas.offsetHeight \* getSetting('Display/Anti-alias'),
>
> false
>
> );
>
> }
>
> }
>
> \
>
>
> function updateCameraAspectRatio() {-0.2
>
> for(let camera of \[leftCamera, rightCamera]) {
>
> let canvas = leftRenderer.domElement;
>
> camera.aspect = canvas.offsetWidth / canvas.offsetHeight;
>
> camera.updateProjectionMatrix();
>
> }
>
> }
>
> \
>
>
> // temp
>
> function startAssistant() {
>
> log('assisstant')
>
> }
>
> \
>
>
> // When settings are loaded, start settings stuff up
>
> function onSettingsLoad() {
>
> // Add sun
>
> sun = new THREE.PointLight(0xffffff, getSetting('Quick Settings/Brightness'));
>
> scene.add(sun);
>
> \
>
>
> // Pointers
>
> pointer = new THREE.Mesh(new THREE.IcosahedronGeometry(getSetting('Display/UI/Pointer/Size'), 1), pointerMaterial);
>
> pointer.name = 'pointer';
>
> scene.add(pointer);
>
> \
>
>
> pointerMaterial = new THREE.MeshBasicMaterial(
>
> {color: "hsl(" + (getSetting('Quick Settings/Theme Hue') + getSetting('Display/UI/Pointer/Hue Shift')) + ", 100%, 50%)"}
>
> );
>
> pointer.material = pointerMaterial;
>
> segmentPointer.material = pointerMaterial;
>
> \
>
>
> // Add wallpaper
>
> let wallpaperURL;
>
> if(getSetting('Display/UI/Wallpaper/Use Direct Link') == true) {
>
> wallpaperURL = getSetting('Display/UI/Wallpaper/Direct Link');
>
> } else {
>
> wallpaperURL = getSetting('Display/UI/Wallpaper/Wallpapers Folder') + '/' + getSetting('Display/UI/Wallpaper/Wallpaper');
>
> }
>
> \
>
>
> loadTexture(wallpaperURL, function(texture) {
>
> let material = new THREE.MeshStandardMaterial({
>
> map: texture,
>
> side: THREE.BackSide,
>
> transparent: true,
>
> opacity: getSetting('Display/UI/Wallpaper/Opacity')
>
> });
>
> \
>
>
> wallpaper = new THREE.Mesh(
>
> new THREE.IcosahedronGeometry(
>
> getSetting('Display/UI/Distance') \* getSetting('Display/UI/Wallpaper/Distance') \* getSetting('Display/UI/Testing Size Multiplier'), 4),
>
> material
>
> );
>
> wallpaper.scale.x = -1;
>
> wallpaper.name = "wallpaper";
>
> scene.add(wallpaper);
>
> });
>
> \
>
>
> // Setup cameras
>
> cameraViewDistance = getSetting('Display/UI/Distance') \* getSetting('Display/UI/Testing Size Multiplier') \* 2; // Keep this down to destroy lag
>
> leftCamera = new THREE.PerspectiveCamera(80, 1, 0.001, cameraViewDistance);
>
> rightCamera = new THREE.PerspectiveCamera(80, 1, 0.001, cameraViewDistance);
>
> \
>
>
> // Setup renderers
>
> leftRenderer = new THREE.WebGLRenderer({canvas: byId('left-canvas'), antialias: true, logarithmicDepthBuffer: true, preserveDrawingBuffer: true, alpha: true});
>
> rightRenderer = new THREE.WebGLRenderer({canvas: byId('right-canvas'), antialias: true, logarithmicDepthBuffer: true, preserveDrawingBuffer: true, alpha: true});
>
> \
>
>
> updateCameraAspectRatio();
>
> setRendererSize();
>
> \
>
>
> window\.addEventListener('resize', function() {
>
> updateCameraAspectRatio();
>
> setRendererSize();
>
> });
>
> \
>
>
> // Setup control center
>
> const baseY = getSetting('Display/Control Center/Switch To Bottom') ? -100 : 100;
>
> const backgroundFolder = getSetting('Display/Control Center/Icon Folder');
>
> function createTileGrid(scale, x, y, farness, tiles, name) {
>
> let counter = 0;
>
> log(tiles)
>
> addWidget({
>
> position: {'x': x, 'y': baseY + y},
>
> size: {'x': 3 \* scale, 'y': 3 \* scale},
>
> background: "hsl(" + getSetting('Quick Settings/Theme Hue') + ", 50%, 80%)",
>
> name: name + ' background',
>
> radius: 0.5 \* scale,
>
> onhover: openTileGroup(name)
>
> });
>
> for(let widgetY = 1; widgetY >= -1; widgetY--) {
>
> for(let widgetX = -1; widgetX <=1; widgetX++) {
>
> if(counter < tiles.length) {
>
> if(tiles\[counter].folder) {
>
> createTileGrid(scale / 3, (widgetX \* scale), (widgetY \* scale), farness \* 0.99, tiles\[counter].children);
>
> } else {
>
> log('scale' + scale)
>
> addWidget({
>
> position: {'x': x + (widgetX \* scale), 'y': baseY + y + (widgetY \* scale)},
>
> size: {'x': scale, 'y': scale},
>
> background: backgroundFolder + '/' + tiles\[counter].icon + '.svg',
>
> name: tiles\[counter].name,
>
> group: name,
>
> radius: scale / 3,
>
> distance: farness \* 0.99
>
> });
>
> log('added widget control center')
>
> };
>
> } else {
>
> break;
>
> };
>
> counter++;
>
> };
>
> if(counter >= tiles.length) {
>
> break;
>
> };
>
> };
>
> };
>
> \
>
>
> createTileGrid(
>
> getSetting('Display/UI/Scale/Control Center') \* 1.5,
>
> 0,
>
> baseY,
>
> 1,
>
> getSetting('Display/Control Center/Tiles'),
>
> getSetting('Display/Control Center/Tiles', false)\['name']
>
> );
>
> // Quick function
>
> let quickFunction = getSetting('Display/Control Center/Quick Function', false);
>
> \
>
>
> addWidget({
>
> position: {'x': 0, 'y': getSetting('Display/Control Center/Switch To Bottom') ? 100 : -100},
>
> background: '/assets/images/icons/control\_center/' + quickFunction.icon + '.svg',
>
> name: quickFunction.name,
>
> onclick: quickFunction.onclick
>
> });
>
> \
>
>
> // testing
>
> addWidget({
>
> position: {'x': 0, 'y': -50},
>
> background: '/assets/images/icons/control\_center/torch.svg',
>
> name: "torch"
>
> });
>
> addWidget({
>
> position: {'x': 200, 'y': 10},
>
> background: '/assets/images/icons/control\_center/screencast.svg',
>
> name: "screencast"
>
> });
>
> for(let i of range(16)) {
>
> addWidget({
>
> position: {'x': i \* 25, 'y': 0},
>
> background: 'hsl(' + getSetting('Quick Settings/Theme Hue') + ', 100%, ' + ((i / 16) \* 100) + '%)',
>
> name: "test" + i,
>
> textContent: '',//i.toString()
>
> 'onclick': function() {
>
> log('click' + i);
>
> },
>
> 'onhover': function() {
>
> log('hover' + i);
>
> }
>
> });
>
> }
>
> };
>
> \
>
>
> function updateSetting(url, value) {
>
> customiseSetting(url, value);
>
> \
>
>
> switch(url) {
>
> case 'Display/UI/Wallpaper/Opacity':
>
> wallpaper.material.opacity = value;
>
> break;
>
> };
>
> };
>
> \
>
>
> // Start
>
> \
>
>
> // Setup the camera stream
>
> function setupCameraStream() {
>
> function handleSuccess(stream) {
>
> cameraStream = document.createElement('video');
>
> cameraStream.style.transform = 'rotate(270deg)';
>
> cameraStream.srcObject = stream;
>
> cameraStream.play();
>
> \
>
>
> let texture = new THREE.VideoTexture(cameraStream);
>
> texture.minFilter = THREE.LinearFilter;
>
> texture.magFilter = THREE.LinearFilter;
>
> scene.background = texture;
>
> \
>
>
> customiseSetting('Display/UI/Wallpaper/Opacity', 0); // Temporary until GUI settings are introduced
>
> }
>
> \
>
>
> function handleError(error) {
>
> // Set wallpaper opacity to 1
>
> updateSetting('Display/UI/Wallpaper/Opacity', 1);
>
> \
>
>
> log('Unable to access the camera/webcam.');
>
> }
>
> \
>
>
> navigator.mediaDevices.getUserMedia({video: {facingMode: "environment"}})
>
> .then(handleSuccess)
>
> .catch(function(error) {
>
> if (error.name === 'OverconstrainedError') {
>
> // Fallback to default video settings
>
> navigator.mediaDevices.getUserMedia({video: true})
>
> .then(handleSuccess)
>
> .catch(handleError);
>
> } else {
>
> // Handle other errors
>
> handleError(error);
>
> }
>
> });
>
> };
>
> \
>
>
> // Fullscreen and pointer lock, request fullscreen mode for the element
>
> function openFullscreen(elem, then) {
>
> if (elem.requestFullscreen) {
>
> elem.requestFullscreen().then(then);
>
> } else if (elem.webkitRequestFullscreen) { /\* Safari \*/
>
> elem.webkitRequestFullscreen().then(then);
>
> } else if (elem.msRequestFullscreen) { /\* IE11 \*/
>
> elem.msRequestFullscreen().then(then);
>
> }
>
> }
>
> // Request pointer lock
>
> function requestPointerLock(myTargetElement) {
>
> const promise = myTargetElement.requestPointerLock({
>
> unadjustedMovement: true,
>
> });
>
> \
>
>
> if (!promise) {
>
> log("disabling mouse acceleration is not supported");
>
> return;
>
> }
>
> \
>
>
> return promise
>
> .then(() => log("pointer is locked"))
>
> .catch((error) => {
>
> if (error.name === "NotSupportedError") {
>
> // Some platforms may not support unadjusted movement.
>
> // You can request again a regular pointer lock.
>
> return myTargetElement.requestPointerLock();
>
> }
>
> });
>
> }
>
> \
>
>
> function lockPointer() {
>
> requestPointerLock(byId('body'));
>
> document.addEventListener("pointerlockchange", lockChangeAlert, false);
>
> };
>
> \
>
>
> function lockChangeAlert() {
>
> if (document.pointerLockElement === byId('body')) {
>
> document.addEventListener("mousemove", updatePosition, false);
>
> } else {
>
> document.removeEventListener("mousemove", updatePosition, false);
>
> }
>
> }
>
> \
>
>
> function updatePosition(e) {
>
> onLockedMouseMove(e.movementX, e.movementY);
>
> };
>
> \
>
>
> function fullscreenAndPointerLock() {
>
> openFullscreen(byId('body'), function() {
>
> lockPointer();
>
> });
>
> }
>
> \
>
>
> function permission() {
>
> // Check if the device supports deviceorientation and requestPermission
>
> if (typeof(DeviceMotionEvent) !== "undefined" && typeof(DeviceMotionEvent.requestPermission) === "function") {
>
> // Request permission
>
> DeviceMotionEvent.requestPermission()
>
> .then(response => {
>
> // Check the response
>
> if (response == "granted") {};
>
> })
>
> .catch(console.error); // Handle errors
>
> } else {
>
> // Device does not support deviceorientation
>
> log("DeviceOrientationEvent is not defined");
>
> }
>
> }
>
> \
>
>
> async function keepScreenAwake() {
>
> // Create a reference for the Wake Lock.
>
> let wakeLock = null;
>
> \
>
>
> // create an async function to request a wake lock
>
> try {
>
> wakeLock = await navigator.wakeLock.request("screen");
>
> log("Wake Lock is active!");
>
> } catch (err) {
>
> // The Wake Lock request has failed - usually system related, such as battery.
>
> log(\`${err.name}, ${err.message}\`);
>
> }
>
> }
>
> \
>
>
> let framesSoFar = 0;
>
> \
>
>
> function startFPSCount() {
>
> byId('logic-fps').innerHTML = fps.toString();
>
> \
>
>
> let renderFPSInterval = setInterval(function() {
>
> byId('render-fps').innerHTML = framesSoFar.toString();
>
> framesSoFar = 0;
>
> }, 1000);
>
> }
>
> \
>
>
> function start() {
>
> byId('loading-screen').style.display = 'none';
>
> setupCameraStream();
>
> startLogic();
>
> startFPSCount();
>
> render();
>
> permission();
>
> fullscreenAndPointerLock();
>
> keepScreenAwake();
>
> \
>
>
> byId('left-canvas').addEventListener('click', fullscreenAndPointerLock);
>
> byId('right-canvas').addEventListener('click', fullscreenAndPointerLock);
>
> };
>
> \
>
>
> // Loading
>
> byId('loading-bar').style.display = 'block';
>
> \
>
>
> manager.onProgress = function (url, itemsLoaded, itemsTotal) {
>
> //log('Loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.');
>
> \
>
>
> byId('loading-bar-fg').style.setProperty('--size', ((itemsLoaded / itemsTotal) \* 100) + '%');
>
> };
>
> \
>
>
> manager.onError = function (url) {
>
> log('There was an error loading ' + url);
>
> };
>
> \
>
>
> manager.onLoad = function ( ) {
>
> setTimeout(function() {
>
> byId('loading-bar').style.display = 'none';
>
> byId('play').style.display = 'block';
>
> }, 500);
>
> byId('play').addEventListener('click', start);
>
> };
>
> \
>
>
> function openTileGroup(group) {
>
> }
>
> \
>
>
> // Logic
>
> let pointerRaycast = new THREE.Raycaster();
>
> let previousIntersection = pointerRaycast.intersectObjects(scene.children);
>
> let clicking = false;
>
> \
>
>
> function logic() {
>
> // Set camera rotation
>
> if(cameraTargetRotation.x != cameraRotation.x || cameraTargetRotation.y != cameraRotation.y) {
>
> setCameraRotation();
>
> };
>
> \
>
>
> // Update pointer
>
> pointerRaycast.set(
>
> new THREE.Vector3(0, 0, 0),
>
> leftCamera.getWorldDirection(new THREE.Vector3())
>
> );
>
> \
>
>
> // Check if the pointer is itersecting with any object
>
> \
>
>
> const intersections = pointerRaycast.intersectObjects(scene.children);
>
> if (intersections.length > 0) {
>
> for(let intersection of intersections) {
>
> // If it's intersecting with itself, don't do anything
>
> if(intersection.object.name == 'pointer' || intersection.object.name == 'segmentPointer') {
>
> return;
>
> } else {
>
> // If it's intersecting with an object, copy that intersection's position, and start to click
>
> const point = intersections\[0].point;
>
> pointer.position.copy(point);
>
> \
>
>
> // Truehover
>
> if(Object.keys(intersection.object).includes('ontruehover')) {
>
> // Prevent hover being continuously trigggered
>
> if(previousIntersection.uuid != intersections\[0].uuid) {
>
> intersection.object.ontruehover();
>
> }
>
> }
>
> log('truehover')
>
> \
>
>
> // Start click after grace period, if object is clickable
>
> if(
>
> Object.keys(intersection.object).includes('onclick') ||
>
> Object.keys(intersection.object).includes('onhover') ||
>
> Object.keys(intersection.object).includes('onhoverexit') ||
>
> Object.keys(intersection.object).includes('onlongpress')
>
> ) {
>
> let gracePeriod = setTimeout(function() {
>
> // onhover
>
> if(Object.keys(intersection.object).includes('onhover')) {
>
> intersection.object.onhover();
>
> }
>
> log('hover')
>
> \
>
>
> // Start click
>
> if(Object.keys(intersection.object).includes('onclick') && (!clicking)) {
>
> clicking = true;
>
> \
>
>
> let fullness = 0;
>
> \
>
>
> // Manage pointers
>
> scene.add(segmentPointer);
>
> segmentPointer.position.copy(pointer);
>
> scene.remove(pointer);
>
> let startClick = setInterval(function() {
>
> fullness += (PI \* 2) / (fps \* getSetting('Input/Eye Click/Duration/Pre-click duration'));
>
> setSegmentPointer(
>
> fullness,
>
> getSetting('Display/UI/Pointer/Size') \* getSetting('Display/UI/Pointer/Clicking Size'),
>
> intersection.object.rotation
>
> );
>
> \
>
>
> byId('pointer-angle').innerHTML = fullness.toFixed(4);
>
> \
>
>
> // On forfeit
>
> let forfeitDistance = distance3d(point, pointerRaycast.intersectObjects(scene.children)\[0].point);
>
> if(forfeitDistance > getSetting('Input/Eye Click/Movement limit')) {
>
> log('forfeit ' + forfeitDistance)
>
> clearInterval(startClick);
>
> afterClick();
>
> }
>
> \
>
>
> if(fullness >= PI \* 2) {
>
> log('click')
>
> intersection.object.onclick();
>
> clearInterval(startClick);
>
> \
>
>
> if(Object.keys(intersection.object).includes('onlongpress')) {
>
> // Start longpress
>
> fullness = 0;
>
> let startLongPress = setInterval(function() {
>
> fullness += (PI \* 2) / (fps \* getSetting('Input/Eye Click/Duration/Long-press duration'));
>
> setSegmentPointer(
>
> fullness,
>
> getSetting('Display/UI/Pointer/Size') \* getSetting('Display/UI/Pointer/Clicking Size'),
>
> intersection.object.rotation,
>
> false
>
> );
>
> byId('pointer-angle').innerHTML = fullness.toFixed(4);
>
> \
>
>
> let forfeitDistance = distance3d(point, pointerRaycast.intersectObjects(scene.children)\[0].point);
>
> if(forfeitDistance > getSetting('Input/Eye Click/Movement limit')) {
>
> log('forfeitlongpress')
>
> clearInterval(startLongPress);
>
> afterClick();
>
> };
>
> \
>
>
> // On click
>
> if(fullness >= PI \* 2) {
>
> intersection.object.onlongpress();
>
> log('longpress')
>
> clearInterval(startLongPress);
>
> afterClick();
>
> }
>
> }, 1000 / fps);
>
> } else {
>
> afterClick();
>
> }
>
> }
>
> }, 1000 / fps);
>
> };
>
> }, getSetting('Input/Eye Click/Delayed hover duration') \* 1000)
>
> return;
>
> } else {
>
> afterClick();
>
> }
>
> \
>
>
> function afterClick() {
>
> // Update previous intersection
>
> previousIntersection = intersections;
>
> previousIntersection.intersection = intersection;
>
> \
>
>
> // Onhoverexit
>
> if(intersection.object.uuid != previousIntersection.intersection.object.uuid) {
>
> previousIntersection.object.onhoverexit();
>
> }
>
> \
>
>
> clicking = false;
>
> log('afterclick')
>
> \
>
>
> // Change back pointers
>
> scene.remove(segmentPointer);
>
> scene.add(pointer);
>
> \
>
>
> return;
>
> }
>
> }
>
> }
>
> };
>
> };
>
> \
>
>
> function startLogic() {
>
> logicInterval = setInterval(logic, 1000 / fps);
>
> };
>
> \
>
>
> function stopLogic() {
>
> clearInterval(logicInterval);
>
> cameraStream.pause();
>
> };
>
> \
>
>
> // Input
>
> function onLockedMouseMove(xMotion, yMotion) {
>
> cameraTargetRotation.x = confine(cameraTargetRotation.x - (yMotion \* cameraRotationSensitivity), -0.5 \* PI, 0.6 \* PI);
>
> if(wrap(cameraTargetRotation.y - (xMotion \* cameraRotationSensitivity), -PI, PI) != (cameraTargetRotation.y - (xMotion \* cameraRotationSensitivity))) {
>
> cameraRotation.y = wrap(cameraTargetRotation.y - (xMotion \* cameraRotationSensitivity), -PI, PI);
>
> };
>
> cameraTargetRotation.y = wrap(cameraTargetRotation.y - (xMotion \* cameraRotationSensitivity), -PI, PI);
>
> setCameraRotation();
>
> };
>
> \
>
>
> // Setup buttons
>
> byId('toggle-debug').addEventListener('click', function(event) {
>
> if(byId('debug-menu').style.display == 'block') {
>
> byId('debug-menu').style.display = 'none';
>
> } else {
>
> byId('debug-menu').style.display = 'block';
>
> }
>
> });
>
> \
>
>
> // Keypress manager
>
> const keysToPreventDefault = \['alt', '/', 'f1', 'f2', 'f3'];
>
> \
>
>
> function putKeyDown(key) {
>
> if(!keysDown.includes(key.toLowerCase())) {
>
> keysDown.push(key.toLowerCase());
>
> };
>
> };
>
> \
>
>
> function putKeyUp(key) {
>
> keysDown = removeFromArray(keysDown, \[key.toLowerCase()]);
>
> };
>
> \
>
>
> document.addEventListener('keydown', function(e) {
>
> if(keysToPreventDefault.includes(e.key.toLowerCase())) {
>
> e.preventDefault();
>
> };
>
> putKeyDown(e.key);
>
> });
>
> \
>
>
> document.addEventListener('keyup', function(e) {
>
> putKeyUp(e.key);
>
> });
>
> \
>
>
> // Pointer position
>
> document.addEventListener('mousemove', function(e) {
>
> pointerPosition = {'x': e.clientX, 'y': e.clientY, 'positions': \[{'clientX': e.clientX, 'clientY': e.clientY}], 'type': 'mouse'};
>
> });
>
> \
>
>
> document.addEventListener('touchmove', function(e) {
>
> pointerPosition = {'x': e.touches\[0].clientX, 'y': e.touches\[0].clientY, 'positions': e.touches, 'type': 'touch'};
>
> });
>
> \
>
>
> // Gyrometer
>
> window\.addEventListener("deviceorientation", function(event) {
>
> orientation = {
>
> 'absolute': event.absolute,
>
> 'alpha': event.alpha,
>
> 'beta': event.beta,
>
> 'gamma': event.gamma
>
> };
>
> \
>
>
> byId('gyro-absolute').innerHTML = orientation.absolute;
>
> byId('gyro-alpha').innerHTML = orientation.alpha.toFixed(2);
>
> byId('gyro-beta').innerHTML = orientation.beta.toFixed(2);
>
> byId('gyro-gamma').innerHTML = orientation.gamma.toFixed(2);
>
> const theOrientation = (window\.offsetWidth > window\.offsetHeight) ? 'landscape' : 'portrait';
>
> \
>
>
> // If orientation is logged correctly
>
> if(!Object.values(orientation).includes(null)) {
>
> // Subtract 90deg if in portrait mode
>
> if(theOrientation == 'portrait') {
>
> orientation.alpha = wrap(orientation.alpha + 90, 0, 360);
>
> }
>
> \
>
>
> // Offset y
>
> const offsetY = 89.5; // I have no idea why this works
>
> \
>
>
> if(Math.abs(orientation.beta) < 100) {
>
> cameraTargetRotation.x = (-orientation.gamma \* (PI / 180) % 180) - offsetY;
>
> } else {
>
> cameraTargetRotation.x = (orientation.gamma \* (PI / 180) % 180) + offsetY;
>
> }
>
> cameraTargetRotation.y = orientation.alpha \* (PI / 180);
>
> cameraTargetRotation.z = (-orientation.beta \* (PI / 180)) + offsetY;
>
> \
>
>
> cameraRotation.x = cameraTargetRotation.x;
>
> cameraRotation.y = cameraTargetRotation.y;
>
> cameraRotation.z = cameraTargetRotation.z;
>
> \
>
>
> if(theOrientation == 'landscape') {
>
> }
>
> \
>
>
> setCameraRotation();
>
> };
>
> }, true);
> ```
>
> :::
cross-posted from: https://lemm.ee/post/46066494
> I followed the recommended processes for adding images to my app, and it is being displayed correctly on the layout preview, but not at all on the app. I have vector assets, webp, png images, but none are being displayed.
>
> The project is too big to put here in its entirety, but please ask for any snippets that could help you solve the issue. I've tried searching the web and asking LLMs and neither could help, so please help me, fellow humans.
The Open Source Initiative (OSI) released the RC1 (“Release Candidate 1” meaning: This thing is basically done and will be released as such unless something catastrophic happens) of the “Open Source AI Definition“.
Some people might wonder why that matters. Some people come up with a bit of writing on AI, what else is new? That’s basically LinkedIn’s whole existence currently. But the OSI has a very special role in the Open Source software ecosystem. Because Open Source isn’t just based on the fact whether you can see code but also about the License that code is covered under: You might get code that you can see but that you are not allowed to touch (think of the recent WinAmp release debate). The OSI basically took on the role of defining which of the different licenses that were being used all over the place actually are “Open Source” and which come with restrictions that undermine the idea.
This is very important: Picking a license is a political act with strong consequences. It can allow or forbid different modes of interaction with an object or might put certain requirements to the use.
I'm working on a python program, and i need to sync the results to an ipad as a todo list (with checkboxes)
I had been using google keep, and manually copying /pasting the data over from my cli based app.
I will be out of the country for 2 weeks, so im updating my software to no longer being cli, and ideally syncing the final list to google keep or something similar, since someone else will be running the software. You know how normies get when they see a terminal window...
I tried this googlekeepapi thing i found online, but the authentication was very complicated and i couldn't get it to work. There is no specific reason we need to use google keep, was just the first thing that came to mind when we set this system up, and it works well and is cloud based.
Do yall know of any service where i can programmatically generate checkbox lists, and sync them over the web?
I should note i do not have a server available to self host. I could potentially spin something up locally with a raspberry pi, but would prefer not to have another potential point of failure.
Hi! I'm having a bit of a struggle with VSCodium. I'm not sure what I did, but I can't access the liveserver in my browser, and the extensions won't load to download them. I keep getting "Error fetching extensions. Failed to fetch" So that's been frustrating. Are there any fixes? What's a good VSCodium alternative I could try?
OGRE has grown to become one of the most popular open-source graphics rendering engines. It’s been 2 years since 2.3.0 and almost a year since the last 2.3.x release. It’s about time for 3.0.0!
- Ogre to OgreNext name migration
- Dealing with ABI mismatches: AbiCookie
- ABI Semver
- Move to C++11 and general cleanup
The GNU Compiler Collection (GCC) support for the C23 programming language standard is now considered "essentially feature-complete" with GCC 15. As such they are preparing to enable the C23 language version (using the GNU23 dialect) by default for the C language version of GCC when not otherwise specified.
Preparations are now underway to set the default C language version of GCC to GNU23 as the GNU dialect of C23. Or in other words, implying -std=gnu23 when no other C standard is specified.
This new edition has been the occasion to overhaul the presentation in many places, but its main purpose is the update to the new C standard, C23. The goal was to publish this new edition of Modern C at the same time as the new C standard goes through the procedure of ISO publication. The closest approximation of the contents of the new standard in a publicly available document can be found here. New releases of major compilers already implement most of the new features that it brings.