👩‍👩‍👧‍👦

複数のSVGの図形が重ならないように制御するJavaScript

2022/03/20に公開

はじめに

描画ツールを作りたくて、JavaScriptでSVGを操作するサンプルを作成中。今回は複数のSVGの図形が重ならないよう制御するサンプルスクリプトを作成。以下はデモ動画

https://youtu.be/SO-hihCUeS0

※サンプル動画にて緑色の図形が青色の図形と重なるときがあります。条件によっては重なっちゃうみたいです。原因わかったら対処します。

スクリプトソース

ソースはgithubに置きました

https://github.com/zgw426/SVG-JavaScript-MySamples

この記事のスクリプトは、11_OverlapControl.html です。

仕組みの説明

このスクリプト11_OverlapControl.htmlの仕組みを紹介します。

図形の重なりを判定する仕組み

図形が重なっているかを判定する仕組みは、こちらのサイトを参考にさせて頂きました。

https://procon.fun/code/line-overlap/

作成した関数がこちら ↓ です

// 重なりを判定
function judgmentOverlap(arg1Start, arg1Width, arg2Start, arg2Width){
    let judResutl = "INITIAL";    // 判定結果
    let rangeVal = 0; // 重なり量
    let l1s = arg1Start;
    let l1e = arg1Start + arg1Width;
    let l2s = arg2Start;
    let l2e = arg2Start + arg2Width;
    let L1 = [ l1s , l1e ];
    let L2 = [ l2s , l2e ];
    let S1 = (L1[0] - L2[0]) * (L1[1] - L2[1]);
    let S2 = (L1[0] - L2[1]) * (L1[1] - L2[0]);

    if(S1 == 0){
        if(S2 < 0){
            judResutl = "OVERLAP"; // 重なる
        }
    }else if(S1 > 0){
        if(S2 < 0){
            judResutl = "OVERLAP"; // 重なる
        }
    }else if(S1 < 0){
        if(S2 <0){
            judResutl = "OVERLAP"; // 重なる(一方に含まれる)
        }
    }
    if(judResutl == "OVERLAP"){
        rangeVal = Math.abs( Math.max(L1[0], L2[0]) - Math.min(L1[1], L2[1]) );
    }
    return [judResutl,rangeVal];
}

この関数の引数は、2つの図形の開始位置と幅になります。X軸方向とY軸方向でそれぞれ重なりの有無を判定します。

図形が重ならない仕組みについて

本スクリプトでは図形が重なっている場合に重ならないように図形を移動します。図形を移動する処理の流れを紹介します。
以下のようにSVGで描画した図形がある状態でどのように処理が行われるか説明します。

青い図形をマウスで選択し、ドラッグで右側に移動したとします。また移動により、青い図形が緑の図形に重なったとします。

図形を移動/変形した場合、図形が重なっていないかの判定と重なっていた場合に図形を移動する処理が発生します。この処理は、選択した図形(今回は青い図形)の左側にある図形と右側の図形でそれぞれ処理します。

重なりが発生した右側の処理について説明します。
重なりの判定は選択した図形に近い図形から順に処理を行います。今回は選択した青い図形に近い緑の図形から処理が行われます。

最初に選択した青い図形を起点とした重なり判定処理を行います。ある図形を起点とした処理は、選択した青い図形に近い順に実行します。

<選択した青い図形を起点とした処理>

緑の図形と青い図形が重なっているか判定し、重なっていると判定されます。判定された場合、青い図形と重ならないように緑の図形を移動します。

その後、青い図形と黄色の図形の重なり判定処理と青い図形と灰色の図形の重なり判定処理を行い、重なっていれば図形を移動します。

<緑の図形を起点とした処理>

次に緑の図形と緑の図形の右側にある図形の重なり判定処理を行い、重なっていれば図形を移動します。
緑の図形は青の図形と重なっていたことで緑の図形の位置が変わっています。それにより、緑の図形と黄色の図形が重なった状態になっています。そのため、重なり制御により黄色の図形が移動されます。

その後、緑の図形と灰色の図形の重なり判定(重なっていれば移動)を行います。ここで緑の図形を起点にした処理は完了します。

その後は、<黄色の図形を起点とした処理>と<灰色の図形を起点とした処理>を実施します。

補足:図形のグループ指定と重なり制御の関係について

作成した図形の重なり制御は、同じグループの図形が重ならないように制御しています。11_OverlapControl.htmlで描画する四角形は変数 objs に設定しています。この変数の group というパラメータがグループを意味します。

11_OverlapControl.htmlでは、以下のように group の値が全て同じ gr1 のため、全ての図形が重ならないように制御されています。

// objs : 描画する四角形のパラメータ
let objs = [
    { "x": 10,  "y": 10, "w":90, "h":90, color: "#ffa500", opacity: "0.8", label: "ORANGE", group: "gr1"},
    { "x": 200, "y": 30, "w":60, "h":80, color: "#0000ff", opacity: "0.9", label: "BLUE", group: "gr1"},
    { "x": 300, "y": 10, "w":50, "h":50, color: "#ff0000", opacity: "0.9", label: "RED", group: "gr1"},
    { "x": 400, "y": 30, "w":50, "h":50, color: "#008000", opacity: "1.0", label: "GREEN", group: "gr1"},
    { "x": 450, "y": 30, "w":40, "h":40, color: "#FF8000", opacity: "1.0", label: "GREEN", group: "gr1"},
    { "x": 500, "y": 30, "w":40, "h":40, color: "#0080FF", opacity: "1.0", label: "HOGE", group: "gr1"},
    { "x": 550, "y": 30, "w":40, "h":40, color: "#FF80FF", opacity: "1.0", label: "FUGA", group: "gr1"},
    { "x": 400, "y": 130, "w":50, "h":50, color: "#AA1155", opacity: "1.0", label: "PIYO", group: "gr1"},
    { "x": 400, "y": 200, "w":50, "h":50, color: "#727272", opacity: "1.0", label: "UNO", group: "gr1"},
]

さいごに

重なり制御の仕組みを作るのに苦労しました。。。バグに悩まされ1か月以上も時間かかってしまいました。GUIツールの作成にむけて更なる機能追加をしていきます。

Discussion