5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【JavaScript】凸包 グラハムスキャンを実装、アニメーション化 する!?【canvas】

Last updated at Posted at 2018-06-23

概要

JavaScriptでグラハムスキャンによってソートされていくアニメーションを実装した。
下の方で、デモとして紹介。

### 参考 [【JavaScript】凸包(グラハムスキャン)を可視化・アニメーション【Canvas】](https://tech-blog.s-yoshiki.com/2018/06/172/) [【JavaScript】凸包(ギフト包装法)を可視化・アニメーション【Canvas】](https://tech-blog.s-yoshiki.com/2018/06/152/)

環境

JavaScript
Canvas
ライブラリとかは利用せず

デモ

とりあえずデモ。
https://jsfiddle.net/s_yoshiki/wntshy3m/show

実装は、もっと下の方で紹介。

凸包

凸包について
凸包 | Wikipedia

様々なアルゴリズムがあるようだが、
その中でも代表的なグラハムスキャンを実装した。

処理のフローは以下の通り。

  1. 点群の中からx,y共に最も小さい点を見つけこれを基準点にする(昇順にソートしたときのindex=0)。
  2. 基準点から残りの点群に対し偏角の順にソートする。
  3. ソートした配列に対しグラハムスキャンを適用
  4. 結果を描画

実際には、
※グラハムスキャンの点の比較に関しては、外積を用いて比較している。
※偏角順のソートは、実際には傾きを利用して計算している。

実装

重要な処理だけ抜粋して紹介。

グラハムスキャン

※偏角順にソートされているものとする

//グラハムスキャン
function grahamScan(map){
    var path = []
    var k = 0
    
    for (var i = 0; i < map.length; ++i) {
        while (true) {
            if (k < 2) {
                break
            }
            var current = [
                map[path[k-1]][0] - map[path[k - 2]][0],
                map[path[k-1]][1] - map[path[k - 2]][1]
            ]
            var next = [
                map[i][0] - map[path[k - 2]][0],
                map[i][1] - map[path[k - 2]][1]
            ]
            
            if (crossVec(current, next) < 0) {
                k--
            } else {
                break
            }
        }
        path[k++] = i
    }
    path = path.slice(0, k)
    path.push(path[0])
    return path
}

//外積
function crossVec(v1,v2){
    return v1[0]*v2[1] - v1[1]*v2[0]
}

昇順ソート

function sortMat(map ,index) {
    	if (index === null || index === undefined) {
        	index = 0
        }
    
    	return map.sort(function(a,b){
        	if( a[index] > b[index] ) {
            	return 1
            } 
            if( a[index] < b[index] ) {
            	return -1
            }
            return 0
        })
    }

偏角順のソート

function sortMatByAngle(map ,p) {

    function norm(v){
        var sum = 0
        for (var j = 0; j < v.length; j++) {
            sum += v[j]*v[j]
        }
        sum = Math.sqrt(sum)
        for (var i = 0; i < v.length; i++) {
            v[i] /= sum
        }
        return v
    }
    
    return map.sort(function(a,b){
        var p1 = [
            p[0] - a[0],
            p[1] - a[1],
        ]
        
        var p2 = [
            p[0] - b[0],
            p[1] - b[1],
        ]
        
        // iOSの場合浮動小数点によるバグが発生する
        if(!navigator.userAgent.match(/(iPhone|iPad|iPod|Android)/)){
            p1 = norm(p1)
            p2 = norm(p2)
        }
        
        if (p1[0] === 0 || p2[0] === 0) {
            return 1
        }
        if( p1[1]/p1[0] > p2[1]/p2[0] ) {
            return 1
        } else {
            return -1
        }
        return 0
    })
}

参考

【JavaScript】凸包(グラハムスキャン)を可視化・アニメーション【Canvas】
【JavaScript】凸包(ギフト包装法)を可視化・アニメーション【Canvas】

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?