알고리즘

Minkowski Sum

조규현15 2024. 4. 26. 16:40
반응형

Minkowski Sum 입니다.

https://en.wikipedia.org/wiki/Minkowski_addition

 

2D Polygon A, B 가 존재할 때 각 Polygon 의 영역을 합한 새로운 영역을 얻습니다.

외각 Vertex 를 얻을 수 있으며 ( 0, 0 ) 을 기준으로 합하므로, 기준 좌표를 옮겨 A, B 도형 위로 옮길 수 있습니다.

<!DOCTYPE html>
<html>

<body>
    <canvas id="myCanvas" width="500" height="500" style="border:1px solid #000000;">
        Your browser does not support the HTML canvas tag.
    </canvas>

    <script>
        // https://cp-algorithms.com/geometry/minkowski.html

        var c = document.getElementById("myCanvas");
        var ctx = c.getContext("2d");

        // prepare for pivot to center
        var width = c.width;
        var height = c.height;
        var half_width = c.width / 2;
        var half_height = c.height / 2;

        function draw_pivot_line() {
            ctx.save();
            ctx.beginPath();

            ctx.moveTo(0, half_height);
            ctx.lineTo(width, half_height);

            ctx.moveTo(half_width, 0);
            ctx.lineTo(half_width, height);

            ctx.stroke();
            ctx.restore();
        }
        draw_pivot_line();

        var scale = 10;
        function draw(from, color) {
            var arr = from.slice(); // deep-copy
            arr.push(arr[0]); // cyclic

            ctx.save();
            ctx.beginPath();
            for (i = 0; i < arr.length - 1; i++) {
                var [x, y] = arr[i];
                var [x2, y2] = arr[i + 1];
                ctx.moveTo(x * scale + half_width, half_height - y * scale);
                ctx.lineTo(x2 * scale + half_width, half_height - y2 * scale);

                ctx.strokeStyle = color;
                ctx.stroke();
            }
            ctx.restore();
        }

        //#region vector utils
        function add(a, b) {
            return [a[0] + b[0], a[1] + b[1]];
        }
        function sub(a, b) {
            return [a[0] - b[0], a[1] - b[1]];
        }
        function cross(a, b) {
            return a[0] * b[1] - a[1] * b[0];
        }
        //#endregion vector utils

        // find center pos from min, max
        function center(poly) {
            var [maxX, maxY] = poly[0];
            var [minX, minY] = poly[0];
            for (var i = 0; i < poly.length; i++) {
                var [x, y] = poly[i]
                if (x > maxX) maxX = x;
                if (y > maxY) maxY = y;
                if (x < minX) minX = x;
                if (y < minY) minY = y;
            }

            return [(maxX + minX) / 2, (maxY + minY) / 2];
        }

        function minkowski(fromA, fromB, pivotToA, pivotToB) {
            var a = fromA.slice(); // deep-copy
            var b = fromB.slice(); // deep-copy

            function reorder_polygon(arr) {
                var p = 0;
                for (var i = 1; i < arr.length; i++) {
                    var [x, y] = arr[i];
                    var [x2, y2] = arr[p];

                    if (y < y2 || (y == y2 && x < x2)) {
                        p = i;
                    }
                }

                // rotate to left
                while (p-- > 0) {
                    arr.push(arr.shift());
                }

                return arr;
            }
            // the first vertex must be the lowest
            a = reorder_polygon(a);
            b = reorder_polygon(b);
            // we must ensure cyclic indexing
            a.push(a[0]);
            a.push(a[1]);
            b.push(b[0]);
            b.push(b[1]);

            var r = [];
            var i = 0; j = 0;
            while (i < a.length - 2 || j < b.length - 2) {
                r.push(add(a[i], b[j]));

                var c = cross(sub(a[i + 1], a[i]), sub(b[j + 1], b[j]));
                if (c >= 0 && i < a.length - 2) {
                    ++i;
                }
                if (c <= 0 && j < b.length - 2) {
                    ++j;
                }
            }
            // option 1.
            if (pivotToA) {
                var ac = center(a);
                for (var k = 0; k < r.length; k++) {
                    r[k] = sub(r[k], ac);
                }

            }
            // option 2.
            else if (pivotToB) {
                var bc = center(b);
                for (var k = 0; k < r.length; k++) {
                    r[k] = sub(r[k], bc);
                }

            }

            return r;
        }

        // test case 1.
        {
            var a = [[3, 8], [3, 6], [6, 6], [6, 8]];
            draw(a, 'black');

            var b = [[9, 9], [11, 9], [11, 11], [9, 11]];
            draw(b, 'black');

            var s = minkowski(a, b);
            draw(s, 'red');

            var s_a = minkowski(a, b, true);
            draw(s_a, 'green');

            var s_b = minkowski(a, b, false, true);
            draw(s_b, 'blue');
        }
        // test case 2.
        {
            var a = [[-4, 2], [-3, 1], [-2, 2]];
            draw(a, 'black');

            var b = [[2, 3], [2, 1], [4, 1], [4, 3]];
            draw(b, 'black');

            var s = minkowski(a, b);
            draw(s, 'red');

            var s_a = minkowski(a, b, true);
            draw(s_a, 'green');

            var s_b = minkowski(a, b, false, true);
            draw(s_b, 'blue');
        }
    </script>
</body>
</html>
반응형

'알고리즘' 카테고리의 다른 글

Alias method Algorithm  (0) 2023.04.27
boids  (0) 2023.04.23
피보나치 힙  (0) 2015.12.15
문자열 매칭 알고리즘  (0) 2015.12.05
A* Algorithm  (0) 2015.11.25