Son-Guhun / Titan-Land-Lands-of-Plenty

A WC3 sandbox roleplaying map inspired by those that came before.
MIT License
10 stars 0 forks source link

Allow different standard deviation for terrain smoothing tool #182

Open Son-Guhun opened 4 years ago

Son-Guhun commented 4 years ago

http://dev.theomader.com/gaussian-kernel-calculator/ https://en.wikipedia.org/wiki/Gaussian_blur

var GaussianKernelCalculator =
{
    init : function()
    {
        var gaussianDistribution = function(x, mu, sigma)
        {
            var d = x - mu;
            var n = 1.0 / (Math.sqrt(2 * Math.PI) * sigma);
            return Math.exp(-d*d/(2 * sigma * sigma)) * n;
        };

        var sampleInterval = function(f, minInclusive, maxInclusive, sampleCount)
        {
            var result = [];
            var stepSize = (maxInclusive - minInclusive) / (sampleCount-1);

            for(var s=0; s<sampleCount; ++s)
            {
                var x = minInclusive + s * stepSize;
                var y = f(x);

                result.push([x, y]);
            }

            return result;
        };

        var integrateSimphson = function(samples)
        {
            var result = samples[0][1] + samples[samples.length-1][1];

            for(var s = 1; s < samples.length-1; ++s)
            {
                var sampleWeight = (s % 2 == 0) ? 2.0 : 4.0;
                result += sampleWeight * samples[s][1];
            }

            var h = (samples[samples.length-1][0] - samples[0][0]) / (samples.length-1);
            return result * h / 3.0;
        };

        var roundTo = function(num, decimals)
        {
            var shift = Math.pow(10, decimals);
            return Math.round(num * shift) / shift;
        };

        var updateKernel = function(sigma, kernelSize, sampleCount)
        {

            var samplesPerBin = Math.ceil(sampleCount / kernelSize);
            if(samplesPerBin % 2 == 0) // need an even number of intervals for simpson integration => odd number of samples
                ++samplesPerBin;

            var weightSum = 0;
            var kernelLeft = -Math.floor(kernelSize/2);

            var calcSamplesForRange = function(minInclusive, maxInclusive)
            {
                return sampleInterval(
                    function(x) {
                        return gaussianDistribution(x, 0, sigma);
                    },
                    minInclusive,
                    maxInclusive,
                    samplesPerBin
                );
            }

            // get samples left and right of kernel support first
            var outsideSamplesLeft  = calcSamplesForRange(-5 * sigma, kernelLeft - 0.5);
            var outsideSamplesRight = calcSamplesForRange(-kernelLeft+0.5, 5 * sigma);

            var allSamples = [[outsideSamplesLeft, 0]];

            // now sample kernel taps and calculate tap weights
            for(var tap=0; tap<kernelSize; ++tap)
            {
                var left = kernelLeft - 0.5 + tap;

                var tapSamples = calcSamplesForRange(left, left+1);
                var tapWeight = integrateSimphson(tapSamples);

                allSamples.push([tapSamples, tapWeight]);
                weightSum += tapWeight;
            }

            allSamples.push([outsideSamplesRight, 0]);

            // renormalize kernel and round to 6 decimals
            for(var i=0; i<allSamples.length; ++i)
            {
                allSamples[i][1] = roundTo(allSamples[i][1] / weightSum, 6);
            }

            // update kernel weights tables
            var weightsTable1d = document.getElementById("GaussianKernelCalculator_kernelWeights1d");
            var weightsTable2d = document.getElementById("GaussianKernelCalculator_kernelWeights2d");

            var tableRow = "<tr>";
            for(var i=1; i<allSamples.length-1; ++i) {
                tableRow += "<td>" + roundTo(allSamples[i][1], 6) + "</td>";
            }
            tableRow += "</tr>"
            weightsTable1d.innerHTML = tableRow;

            var tableData = "";
            for(var i=1; i<allSamples.length-1; ++i) {
                tableData += "<tr>";
                for(var j=1; j<allSamples.length-1; ++j) {
                    tableData += "<td>" + roundTo(allSamples[i][1] * allSamples[j][1], 6) + "</td>";
                }
                tableData += "</tr>"
            }
            weightsTable2d.innerHTML = tableData;

            // area outside kernel Support
            var errorField = document.getElementById("GaussianKernelCalculator_approximationError");
            errorField.innerHTML = roundTo((1.0 - weightSum) * 100.0, 2) + "%";

            // Create and populate the data table.
            var chartData = new google.visualization.DataTable();
            chartData.addColumn('number','Pixel');
            chartData.addColumn('number','Continuous Distribution');
            chartData.addColumn('number', 'Discrete Kernel');

            for(var tap = 0; tap < allSamples.length; ++tap)
            {
                var tapSamples = allSamples[tap][0];
                var tapWeight = allSamples[tap][1];

                for(var s=0; s<tapSamples.length; ++s)
                {
                    chartData.addRow([tapSamples[s][0], tapSamples[s][1], tapWeight]);
                }

                var lastRow = tapSamples[tapSamples.length-1];
                chartData.addRow([lastRow[0], lastRow[1], 0]);
            }

            // finally draw the chart
            var chartOptions =
            {
                'vAxis': {title: "Kernel Weight"},
                'hAxis': {title: "Pixel"},
                'seriesType': "area",
                'series': {1: {type: "line"}},
            };

            var chart = new google.visualization.ChartWrapper(
            {
                chartType: 'ComboChart',
                dataTable: chartData,
                options:
                {
                    'vAxis': {title: "Kernel Weight"},
                    'hAxis': {title: "Pixel"},
                    'seriesType': "area",
                    'series': {1: {type: "line"}},
                },
                containerId: 'GaussianKernelCalculator_distributionChart'
            });

            chart.draw();
        };

        var showKernel = function(show)
        {
            document.getElementById("GaussianKernelCalculator_result").style.display = show ? 'block' : 'none';
            document.getElementById("GaussianKernelCalculator_inputError").style.display = show ? 'none' : 'block';
        };

        var updatePage = function()
        {
            var sigma = parseFloat(document.getElementById("GaussianKernelCalculator_sigma").value);
            var kernelSize = parseInt(document.getElementById("GaussianKernelCalculator_kernelSize").value);
            var sampleCount =  1000.0;

            showKernel(false);

            // parameter validation
            if(sigma<=0 || isNaN(sigma))
            {
                document.getElementById("GaussianKernelCalculator_inputError").innerHTML = "Invalid Sigma: Please enter a positive number greater than zero";
            }
            else if(kernelSize<=0 || isNaN(kernelSize) || kernelSize%2==0 || kernelSize>999)
            {
                document.getElementById("GaussianKernelCalculator_inputError").innerHTML = "Invalid Kernel Size: Please enter an odd positive integer smaller than 999";
            }
            else
            {
                showKernel(true);
                updateKernel(sigma, kernelSize, sampleCount);
            }
        };

        GaussianKernelCalculator.updatePage = updatePage;
    },

    drawPrecomputedKernel : function()
    {
        var chartDataJSON= {
            "containerId":"GaussianKernelCalculator_distributionChart",
            "dataTable": {
                "cols":[
                        {"id":"","label":"Pixel","pattern":"","type":"number","p":{}},
                        {"id":"","label":"Continuous Distribution","pattern":"","type":"number","p":{}},
                        {"id":"","label":"Discrete Kernel","pattern":"","type":"number"}
                        ],
                "rows":[
                        {"c":[{"v":-5,"f":null},{"v":0.0000014867195147342977,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":-4.75,"f":null},{"v":0.000005029507288592445,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":-4.5,"f":null},{"v":0.000015983741106905475,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":-4.25,"f":null},{"v":0.00004771863654120495,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":-4,"f":null},{"v":0.00013383022576488537,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":-3.75,"f":null},{"v":0.0003525956823674454,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":-3.5,"f":null},{"v":0.0008726826950457602,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":-3.25,"f":null},{"v":0.002029048057299768,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":-3,"f":null},{"v":0.0044318484119380075,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":-2.75,"f":null},{"v":0.009093562501591053,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":-2.5,"f":null},{"v":0.01752830049356854,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":-2.5,"f":null},{"v":0.01752830049356854,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":-2.5,"f":null},{"v":0.01752830049356854,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":-2.4,"f":null},{"v":0.0223945302948429,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":-2.3,"f":null},{"v":0.028327037741601186,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":-2.2,"f":null},{"v":0.035474592846231424,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":-2.1,"f":null},{"v":0.04398359598042719,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":-2,"f":null},{"v":0.05399096651318806,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":-1.9,"f":null},{"v":0.0656158147746766,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":-1.7999999999999998,"f":null},{"v":0.07895015830089418,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":-1.7,"f":null},{"v":0.09404907737688695,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":-1.6,"f":null},{"v":0.11092083467945554,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":-1.5,"f":null},{"v":0.12951759566589174,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":-1.5,"f":null},{"v":0.12951759566589174,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":-1.5,"f":null},{"v":0.12951759566589174,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":-1.4,"f":null},{"v":0.14972746563574488,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":-1.3,"f":null},{"v":0.17136859204780736,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":-1.2,"f":null},{"v":0.19418605498321295,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":-1.1,"f":null},{"v":0.21785217703255053,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":-1,"f":null},{"v":0.24197072451914337,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":-0.8999999999999999,"f":null},{"v":0.26608524989875487,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":-0.7999999999999999,"f":null},{"v":0.2896915527614828,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":-0.7,"f":null},{"v":0.31225393336676127,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":-0.6,"f":null},{"v":0.33322460289179967,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":-0.5,"f":null},{"v":0.3520653267642995,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":-0.5,"f":null},{"v":0.3520653267642995,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":-0.5,"f":null},{"v":0.3520653267642995,"f":null},{"v":0.387741,"f":null}]},
                        {"c":[{"v":-0.4,"f":null},{"v":0.36827014030332333,"f":null},{"v":0.387741,"f":null}]},
                        {"c":[{"v":-0.3,"f":null},{"v":0.38138781546052414,"f":null},{"v":0.387741,"f":null}]},
                        {"c":[{"v":-0.19999999999999996,"f":null},{"v":0.39104269397545594,"f":null},{"v":0.387741,"f":null}]},
                        {"c":[{"v":-0.09999999999999998,"f":null},{"v":0.3969525474770118,"f":null},{"v":0.387741,"f":null}]},
                        {"c":[{"v":0,"f":null},{"v":0.3989422804014327,"f":null},{"v":0.387741,"f":null}]},
                        {"c":[{"v":0.10000000000000009,"f":null},{"v":0.3969525474770118,"f":null},{"v":0.387741,"f":null}]},
                        {"c":[{"v":0.20000000000000007,"f":null},{"v":0.3910426939754559,"f":null},{"v":0.387741,"f":null}]},
                        {"c":[{"v":0.30000000000000004,"f":null},{"v":0.3813878154605241,"f":null},{"v":0.387741,"f":null}]},
                        {"c":[{"v":0.4,"f":null},{"v":0.36827014030332333,"f":null},{"v":0.387741,"f":null}]},
                        {"c":[{"v":0.5,"f":null},{"v":0.3520653267642995,"f":null},{"v":0.387741,"f":null}]},
                        {"c":[{"v":0.5,"f":null},{"v":0.3520653267642995,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":0.5,"f":null},{"v":0.3520653267642995,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":0.6,"f":null},{"v":0.33322460289179967,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":0.7,"f":null},{"v":0.31225393336676127,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":0.8,"f":null},{"v":0.28969155276148273,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":0.9,"f":null},{"v":0.2660852498987548,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":1,"f":null},{"v":0.24197072451914337,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":1.1,"f":null},{"v":0.21785217703255053,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":1.2000000000000002,"f":null},{"v":0.19418605498321292,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":1.3,"f":null},{"v":0.17136859204780736,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":1.4,"f":null},{"v":0.14972746563574488,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":1.5,"f":null},{"v":0.12951759566589174,"f":null},{"v":0.24477,"f":null}]},
                        {"c":[{"v":1.5,"f":null},{"v":0.12951759566589174,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":1.5,"f":null},{"v":0.12951759566589174,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":1.6,"f":null},{"v":0.11092083467945554,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":1.7,"f":null},{"v":0.09404907737688695,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":1.8,"f":null},{"v":0.07895015830089415,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":1.9,"f":null},{"v":0.0656158147746766,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":2,"f":null},{"v":0.05399096651318806,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":2.1,"f":null},{"v":0.04398359598042719,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":2.2,"f":null},{"v":0.035474592846231424,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":2.3,"f":null},{"v":0.028327037741601186,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":2.4,"f":null},{"v":0.0223945302948429,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":2.5,"f":null},{"v":0.01752830049356854,"f":null},{"v":0.061359,"f":null}]},
                        {"c":[{"v":2.5,"f":null},{"v":0.01752830049356854,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":2.5,"f":null},{"v":0.01752830049356854,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":2.75,"f":null},{"v":0.009093562501591053,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":3,"f":null},{"v":0.0044318484119380075,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":3.25,"f":null},{"v":0.002029048057299768,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":3.5,"f":null},{"v":0.0008726826950457602,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":3.75,"f":null},{"v":0.0003525956823674454,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":4,"f":null},{"v":0.00013383022576488537,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":4.25,"f":null},{"v":0.00004771863654120495,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":4.5,"f":null},{"v":0.000015983741106905475,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":4.75,"f":null},{"v":0.000005029507288592445,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":5,"f":null},{"v":0.0000014867195147342977,"f":null},{"v":0,"f":null}]},
                        {"c":[{"v":5,"f":null},{"v":0.0000014867195147342977,"f":null},{"v":0,"f":null}]}
                        ],
                "p":null
            },
            "options":{
                "vAxis":{"title":"Kernel Weight"},
                "hAxis":{"title":"Pixel"},
                "seriesType":"area",
                "series":{"1":{"type":"line"}}
            },
            "state":{},
            "isDefaultVisualization":true,
            "chartType":"ComboChart"
        };

        var chart = new google.visualization.ChartWrapper(chartDataJSON);
        chart.draw();
    }
};

google.load('visualization', '1', {packages: ['corechart']});
google.setOnLoadCallback(function()
{
    GaussianKernelCalculator.init();

    // draw precalculated kernel to improve page loading times
    GaussianKernelCalculator.drawPrecomputedKernel();
});