unityController - Utility Methods

This section includes all the functions that are useful to the sample, but don’t involve any functions from the Genvid API.

popularityToText

popularityToText(popularity)

The popularityToText method converts a popularity value to a text value.

        // Convert popularity value to popularity text
        popularityToText(popularity) {
            let hearts = ["💔", "♡", "♥", "💕"];
            let levels = [0.1, 1.0, 5.0];
            for (let i = 0; i < hearts.length; ++i) {
                if (popularity < levels[i]) {
                    return hearts[i];
                }
            }
            return hearts[levels.length];
        }

removeFromArray

removeFromArray(arr, item)

The removeFromArray method removes a value from an array sent as parameters.

        // Removes all instances of @item in @arr, returning true if the element was removed.
        removeFromArray(arr, item) {
            let removedSomething = false;
            for (let i = arr.length - 1; i >= 0; i--) {
                if (arr[i] === item) {
                    arr.splice(i, 1);
                    removedSomething = true;
                }
            }
            return removedSomething;
        }

makeCircleZ

makeCircleZ(dst: number[], x: number, y: number, z: number, r: number, c: genvidMath.IVec4)

The makeCircleZ method prepares a circle on the XY plane centered at coordinates x,y,z of radius r and color c. All these values are sent as parameters to the method. We use it to determine the correct positions for each WebGL circle.

        // Utility routine preparing a circle on the XY-plane
        // centered at {x,y,z}, of radius r and color c.
        makeCircleZ(dst: number[], x: number, y: number, z: number, r: number, c: genvidMath.IVec4) {
            dst.push(
                // X   Y   Z     U    V      R    G    B    A
                x - r, y - r, z, 0.0, 0.0, c.x, c.y, c.z, c.w,
                x + r, y - r, z, 1.0, 0.0, c.x, c.y, c.z, c.w,
                x + r, y + r, z, 1.0, 1.0, c.x, c.y, c.z, c.w,
                x - r, y + r, z, 0.0, 1.0, c.x, c.y, c.z, c.w
            );
        }

projectWithRadius

projectWithRadius(mat: genvidMath.IMat4, pos_3d: genvidMath.IVec3, rad_3d: number): [genvidMath.IVec3, number]

The projectWithRadius method converts a 3D radius around a 3D position using the viewProjection matrix. We use it to determine a hitbox for each circle when trying to click on it.

        // Converts a @rad_3d around a 3D position @pos_3d using the viewProjection matrix @mat.
        // Returns an array [pos_2d, rad_2d].
        projectWithRadius(mat: genvidMath.IMat4, pos_3d: genvidMath.IVec3, rad_3d: number): [genvidMath.IVec3, number] {
            // There might be a more mathematically sound solution to this,
            // but I've decided to use the shotgun approach and just project
            // 8 positions (add/sub radius to every dimension), and keep
            // the one which yields the largest 2D distance.
            let pos_2d = genvidMath.projectPosition(mat, pos_3d);
            let rad_sq_2d = 0;
            let offsets = [
                genvidMath.vec3(rad_3d, 0, 0),
                genvidMath.vec3(-rad_3d, 0, 0),
                genvidMath.vec3(0, rad_3d, 0),
                genvidMath.vec3(0, -rad_3d, 0),
                genvidMath.vec3(0, 0, rad_3d),
                genvidMath.vec3(0, 0, -rad_3d),
            ];
            for (let i = 0; i < offsets.length; ++i) {
                let off = offsets[i];
                let n_pos_3d = genvidMath.add3D(pos_3d, off);
                let n_pos_2d = genvidMath.projectPosition(mat, n_pos_3d);
                let n_rad_sq_2d = genvidMath.lengthSq2D(genvidMath.sub2D(pos_2d, n_pos_2d));
                rad_sq_2d = Math.max(rad_sq_2d, n_rad_sq_2d);
            }
            return [pos_2d, Math.sqrt(rad_sq_2d)];
        }

center_at

center_at(html_element: HTMLElement, pos_2d: genvidMath.IVec2, offset_pixels: genvidMath.IVec2)

The center_at method modifies an HTML element position to be centered at the 2D position sent. We use it to move the name div-element displayed above each object.

        // Change the HTML element position to be at the center of the pos 2d sent
        center_at(html_element: HTMLElement, pos_2d: genvidMath.IVec2, offset_pixels: genvidMath.IVec2) {

            // Convert from [-1, 1] range to [0, 1].
            let vh = genvidMath.vec2(0.5, 0.5);
            let pos_2d_n = genvidMath.mad2D(pos_2d, vh, vh);

            // Convert from [0, 1] range to [0, w].
            let p = html_element.parentElement;
            let p_size = genvidMath.vec2(p.clientWidth, p.clientHeight);
            let pos_in_parent = genvidMath.mul2D(pos_2d_n, p_size);

            // Adjust for centering element.
            let e_size = genvidMath.vec2(html_element.clientWidth, html_element.clientHeight);
            let e_offset = genvidMath.muls2D(e_size, -0.5);
            let pos_centered = genvidMath.add2D(pos_in_parent, e_offset);

            // Apply user offset.
            let pos_final = genvidMath.sub2D(pos_centered, offset_pixels);
            $(html_element).css({ left: pos_final.x, bottom: pos_final.y, position: "absolute", "z-index": "1100" });
        }

preN

preN(str: string, n: number): string

The preN method adds empty spaces to a string. We use it to correctly display the data on the Genvid overlay.

        // Widens a string to at least n characters, prefixing with spaces.
        private preN(str: string, n: number): string {
            let s: number = str.length;
            if (s < n) {
                str = " ".repeat(n - s) + str;
            }
            return str;
        }

convertMatrix

convertMatrix(rawmat)

The convertMatrix method converts an array of numbers into a Genvid Math 4x4 Matrix. We use it when doing operations that need to use the proper matrix-format, since we recieve the data sent from the game as an array of numbers.

        // Convert a 4x4 matrix from the format used by Unity (16 labeled scalars) into a GenvidMath.mat4 instance.
        convertMatrix(rawmat) {
            return genvidMath.mat4(
                genvidMath.vec4(rawmat.e00, rawmat.e01, rawmat.e02, rawmat.e03),
                genvidMath.vec4(rawmat.e10, rawmat.e11, rawmat.e12, rawmat.e13),
                genvidMath.vec4(rawmat.e20, rawmat.e21, rawmat.e22, rawmat.e23),
                genvidMath.vec4(rawmat.e30, rawmat.e31, rawmat.e32, rawmat.e33)
            );
        }

findOrCreateTagDiv

findOrCreateTagDiv(cube: IGameDataCube): HTMLElement

The findOrCreateTagDiv method finds or creates the div that displays the name of the object that is moves with the object.

        // Find or create the div needed for the moving name on the overlay
        findOrCreateTagDiv(cube: IGameDataCube): HTMLElement {
            let elem_id: string = "cube_tag_" + cube.name;
            let elem: HTMLElement = <HTMLDivElement>document.querySelector("#" + elem_id);
            if (elem == null) {
                elem = document.createElement("div");
                elem.id = elem_id;
                elem.textContent = cube.name;
                elem.classList.add("tag");

                let parent: HTMLElement = <HTMLDivElement>document.querySelector("#video_overlay");
                parent.appendChild(elem);

                elem.addEventListener("click", (_event) => { this.setSelection(elem.textContent); this.onSelect(elem.textContent, false); }, false);
            }
            return elem;
        }

msToDuration

msToDuration(duration: number): string

The msToDuration method converts milliseconds into days, hours, minutes, and seconds.

        // Method used to convert ms to specific duration
        private msToDuration(duration: number): string {
            let res = "";
            if (duration < 0) {
                res += "-";
                duration = -duration;
            }
            let second = 1000;
            let minute = second * 60;
            let hour = minute * 60;
            let day = hour * 24;
            let started = false;
            if (duration > day) {
                started = true;
                let rest = duration % day;
                let days = (duration - rest) / day;
                duration = rest;
                res += `${days}:`;
            }
            if (started || duration > hour) {
                started = true;
                let rest = duration % hour;
                let hours = (duration - rest) / hour;
                duration = rest;
                if (hours < 10) {
                    res += "0";
                }
                res += `${hours}:`;
            }
            if (started || duration > minute) {
                started = true;
                let rest = duration % minute;
                let minutes = (duration - rest) / minute;
                duration = rest;
                if (minutes < 10) {
                    res += "0";
                }
                res += `${minutes}:`;
            }
            if (started || duration > second) {
                let rest = duration % second;
                let seconds = (duration - rest) / second;
                duration = rest;
                if (seconds < 10) {
                    res += "0";
                }
                res += `${seconds}.`;
            } else {
                res += "0.";
            }
            if (duration < 100) {
                res += "0";
            }
            if (duration < 10) {
                res += "0";
            }
            return res + `${duration}`;
        }

checkFullScreen

checkFullScreen(): boolean

The checkFullScreen method returns the current fullscreen status.

        checkFullScreen(): boolean {
            let doc = <any>document;
            return doc.fullscreenElement || doc.webkitFullscreenElement || doc.mozFullScreenElement || doc.msFullscreenElement;
        }