utils/PlainSegmentWorker.worker.jsjavascript
"use strict";

let model = null;

let cached_ix = null;
let cached_iy = null;
let v00 = null;
let v01 = null;
let v10 = null;
let v11 = null;
//let t = null;

function rawval(ix, iy) {
    if (iy < 0) {
        iy = -iy;
        ix += model.width / 2;
    } else if (iy >= model.height) {
        iy = 2 * (model.height - 1) - iy;
        ix += model.width / 2;
    }

    if (ix < 0) {
        ix += model.width;
    } else if (ix >= model.width) {
        ix -= model.width;
    }

    var k = (iy * model.width + ix) * 2 + model.i;

    return (model.rawfile[k] << 8) | model.rawfile[k + 1];
}

function getHeightMSL(lon, lat) {
    if (!model) return 0;

    if (lon < 0) lon += 360.0;

    var fy = (90 - lat) * model.rlatres;
    var fx = lon * model.rlonres;
    var iy = Math.floor(fy);
    var ix = Math.floor(fx);

    fx -= ix;
    fy -= iy;

    if (iy === model.height - 1) {
        iy--;
    }

    if (cached_ix !== ix || cached_iy !== iy) {
        cached_ix = ix;
        cached_iy = iy;

        v00 = rawval(ix, iy);
        v01 = rawval(ix + 1, iy);
        v10 = rawval(ix, iy + 1);
        v11 = rawval(ix + 1, iy + 1);
    }

    let h = null;

    var a = (1 - fx) * v00 + fx * v01;
    var b = (1 - fx) * v10 + fx * v11;

    h = (1 - fy) * a + fy * b;

    return model.offset + model.scale * h;
}

const HALF_PI = Math.PI * 0.5;
const POLE = 20037508.34;
const PI_BY_POLE = Math.PI / POLE;
const INV_POLE_BY_180 = 180.0 / POLE;
const INV_PI_BY_180 = 180.0 / Math.PI;
const INV_PI_BY_180_HALF_PI = INV_PI_BY_180 * HALF_PI;
const RADIANS = Math.PI / 180.0;
const INV_PI_BY_360 = INV_PI_BY_180 * 2.0;

let E2 = 0.0,
    A = 0.0;

let _projFunc = null;

const Vec3 = function (x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
};

var geodeticToCartesian = function (lon, lat, heightFactor, res) {
    let h = getHeightMSL(lon, lat) * heightFactor;

    let latrad = RADIANS * lat,
        lonrad = RADIANS * lon;

    let slt = Math.sin(latrad);

    let N = A / Math.sqrt(1.0 - E2 * slt * slt);
    let nc = (N + h) * Math.cos(latrad);

    res.x = nc * Math.cos(lonrad);
    res.y = nc * Math.sin(lonrad);
    res.z = (N * (1 - E2) + h) * slt;
};

var geodeticToCartesianInverse = function (lon, lat, heightFactor, res) {
    geodeticToCartesian(
        lon * INV_POLE_BY_180,
        INV_PI_BY_360 * Math.atan(Math.exp(lat * PI_BY_POLE)) - INV_PI_BY_180_HALF_PI,
        heightFactor,
        res
    );
};

var worldPos = new Vec3(0.0, 0.0, 0.0);
var rtcPos = new Vec3(0.0, 0.0, 0.0);
var _tempHigh = new Vec3(0.0, 0.0, 0.0);
var _tempLow = new Vec3(0.0, 0.0, 0.0);

var doubleToTwoFloats = function (v, high, low) {
    let x = v.x,
        y = v.y,
        z = v.z;

    var doubleHigh;

    if (x >= 0.0) {
        doubleHigh = Math.floor(x / 65536.0) * 65536.0;
        high.x = Math.fround(doubleHigh);
        low.x = Math.fround(x - doubleHigh);
    } else {
        doubleHigh = Math.floor(-x / 65536.0) * 65536.0;
        high.x = Math.fround(-doubleHigh);
        low.x = Math.fround(x + doubleHigh);
    }

    if (y >= 0.0) {
        doubleHigh = Math.floor(y / 65536.0) * 65536.0;
        high.y = Math.fround(doubleHigh);
        low.y = Math.fround(y - doubleHigh);
    } else {
        doubleHigh = Math.floor(-y / 65536.0) * 65536.0;
        high.y = Math.fround(-doubleHigh);
        low.y = Math.fround(y + doubleHigh);
    }

    if (z >= 0.0) {
        doubleHigh = Math.floor(z / 65536.0) * 65536.0;
        high.z = Math.fround(doubleHigh);
        low.z = Math.fround(z - doubleHigh);
    } else {
        doubleHigh = Math.floor(-z / 65536.0) * 65536.0;
        high.z = Math.fround(-doubleHigh);
        low.z = Math.fround(z + doubleHigh);
    }
};

self.onmessage = function (msg) {
    if (msg.data.model) {
        model = msg.data.model;
        model.rawfile = msg.data.rawfile;
    } else if (msg.data.params) {
        let rtc_x = msg.data.params[14];
        let rtc_y = msg.data.params[15];
        let rtc_z = msg.data.params[16];

        let xmin = 549755748352.0,
            xmax = -549755748352.0,
            ymin = 549755748352.0,
            ymax = -549755748352.0,
            zmin = 549755748352.0,
            zmax = -549755748352.0;

        E2 = msg.data.params[8];
        A = msg.data.params[9];

        let gridSize = msg.data.params[2],
            fgs = msg.data.params[3],
            r2_x = msg.data.params[10],
            r2_y = msg.data.params[11],
            r2_z = msg.data.params[12];

        let heightFactor = msg.data.params[13];

        if (msg.data.params[1] === 0.0) {
            _projFunc = geodeticToCartesianInverse;
        } else {
            _projFunc = geodeticToCartesian;
        }

        let maxFgs = Math.max(fgs, gridSize);
        let llStep = (msg.data.params[6] - msg.data.params[4]) / maxFgs;
        let ltStep = (msg.data.params[7] - msg.data.params[5]) / maxFgs;

        let esw_lon = msg.data.params[4],
            ene_lat = msg.data.params[7];

        let dg = Math.max(fgs / gridSize, 1.0),
            gs = maxFgs + 1;

        const gsgs = gs * gs;

        const gridSize3 = (gridSize + 1) * (gridSize + 1) * 3;

        let plainNormals = new Float32Array(gridSize3);

        let plainVertices = new Float64Array(gridSize3);
        let plainVerticesHigh = new Float32Array(gridSize3);
        let plainVerticesLow = new Float32Array(gridSize3);
        let normalMapNormals = new Float32Array(gsgs * 3);
        let normalMapVertices = new Float64Array(gsgs * 3);

        let ind = 0,
            nmInd = 0;

        for (let k = 0; k < gsgs; k++) {
            let j = k % gs,
                i = ~~(k / gs);

            _projFunc(esw_lon + j * llStep, ene_lat - i * ltStep, heightFactor, worldPos);

            let nx = worldPos.x * r2_x,
                ny = worldPos.y * r2_y,
                nz = worldPos.z * r2_z;
            let l = 1.0 / Math.sqrt(nx * nx + ny * ny + nz * nz);
            let nxl = nx * l,
                nyl = ny * l,
                nzl = nz * l;

            rtcPos.x = worldPos.x - rtc_x;
            rtcPos.y = worldPos.y - rtc_y;
            rtcPos.z = worldPos.z - rtc_z;

            normalMapVertices[nmInd] = rtcPos.x;
            normalMapNormals[nmInd++] = nxl;

            normalMapVertices[nmInd] = rtcPos.y;
            normalMapNormals[nmInd++] = nyl;

            normalMapVertices[nmInd] = rtcPos.z;
            normalMapNormals[nmInd++] = nzl;

            if (i % dg === 0 && j % dg === 0) {
                doubleToTwoFloats(rtcPos, _tempHigh, _tempLow);

                plainVertices[ind] = rtcPos.x;
                plainVerticesHigh[ind] = _tempHigh.x;
                plainVerticesLow[ind] = _tempLow.x;
                plainNormals[ind++] = nxl;

                plainVertices[ind] = rtcPos.y;
                plainVerticesHigh[ind] = _tempHigh.y;
                plainVerticesLow[ind] = _tempLow.y;
                plainNormals[ind++] = nyl;

                plainVertices[ind] = rtcPos.z;
                plainVerticesHigh[ind] = _tempHigh.z;
                plainVerticesLow[ind] = _tempLow.z;
                plainNormals[ind++] = nzl;

                if (worldPos.x < xmin) xmin = worldPos.x;
                if (worldPos.x > xmax) xmax = worldPos.x;
                if (worldPos.y < ymin) ymin = worldPos.y;
                if (worldPos.y > ymax) ymax = worldPos.y;
                if (worldPos.z < zmin) zmin = worldPos.z;
                if (worldPos.z > zmax) zmax = worldPos.z;
            }
        }

        let x = (xmax - xmin) * 0.5,
            y = (ymax - ymin) * 0.5,
            z = (zmax - zmin) * 0.5;

        let plainRadius = Math.sqrt(x * x + y * y + z * z);

        self.postMessage(
            {
                id: msg.data.params[0],
                plainVertices: plainVertices,
                plainVerticesHigh: plainVerticesHigh,
                plainVerticesLow: plainVerticesLow,
                plainNormals: plainNormals,
                normalMapNormals: normalMapNormals,
                normalMapVertices: normalMapVertices,
                plainRadius: plainRadius,
                relativeCenter: [rtc_x, rtc_y, rtc_z]
            },
            [
                plainVertices.buffer,
                plainVerticesHigh.buffer,
                plainVerticesLow.buffer,
                plainNormals.buffer,
                normalMapNormals.buffer,
                normalMapVertices.buffer
            ]
        );
    }
};