Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow different standard deviation for terrain smoothing tool #182

Open
Son-Guhun opened this issue Feb 25, 2020 · 0 comments
Open

Allow different standard deviation for terrain smoothing tool #182

Son-Guhun opened this issue Feb 25, 2020 · 0 comments
Assignees
Labels
enhancement New feature or request trigger-editor Stuff that needs coding
Milestone

Comments

@Son-Guhun
Copy link
Owner

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();
});
@Son-Guhun Son-Guhun added enhancement New feature or request trigger-editor Stuff that needs coding labels Feb 25, 2020
@Son-Guhun Son-Guhun added this to the Version 1.5.x milestone Feb 25, 2020
@Son-Guhun Son-Guhun self-assigned this Feb 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request trigger-editor Stuff that needs coding
Projects
None yet
Development

No branches or pull requests

1 participant