ワードクラウドを自作する②

ワードクラウドを自作する②

ワードクラウドを自作する②

前回C#でワードクラウドを自作しました。しかし、文字が重なったり、はみ出してしまったりと課題は色々とありました。なので今回は前回出た課題を解決し、より見やすいワードクラウドに改善したいと思います。
前回の画像がこちら

問題は描画する左上の座標のみしか指定していないこと、描画した場所を記録していないため重なる描画をしてしまうことです。そのため。描画する文字が縦横どのくらいの長さになるのか計測することと、描画場所を記録できる仕組みを作る必要があります。
描画する文字列の長さはMeasureStringメソッドを使用し縦横の長さを取得することができるため、このメソッドを利用したいと思います。また、描画場所については2次元配列で管理する方針で進めたいと思います。
なおMeasureStringメソッドを使用するためには、一度Graphicsに描画するため、計測用のGraphicsを用意します。


文字がはみ出さないようにする

まずは、「文字がはみ出す問題」を解決したいと思います。
文字がはみ出してしまう原因は、描画する文字列のx座標とy座標の終了値が生成する画像の右のx座標と下のy座標を超えてしまっていることです。
なので描画する前に、描画予定の文字列の長さを取得し右上と左下の座標が描画範囲に収まっていることを確認します。そのうえで描画可能であれば文字列の描画を行います。
コードで実現すると以下のようになります。(長さを取得するために描画するBitmap(demoG)を用意しています。)

 var size2 = demoG.MeasureString(str, fontData);
 if(height <= size2.Height+y )
 {
    return false;                           // 描画できないのでfalseを返す
 }
 else if(width <= size2.Width+x)
 {
    return false;                           // 描画できないのでfalseを返す
 }
        
MeasureStringメソッドの戻り値は、WidthHeightを指定し縦横のデータを取得できます。 しかし、取得したWidthとHeightは描画する文字の縦横の長さであるため、描画するx, y座標と計算してx座標とy座標の終了値を求めます。
取得したx, y座標の終了値と描画範囲であるwidthとheightと比較し、生成する画面サイズ以内に収まっているかを確認します。収まっていない場合は、falseを返しその座標では描画できないことを示します。


文字が重ならないようにする

次に、「文字が重なる問題」を解決したいと思います。
文字が重なってしまう原因は、文字を描画する座標を乱数で指定しているためですが、描画する文字数や大きさはわからないため、あらかじめ描画する位置を指定することができません。 なので描画する座標は乱数で指定する方針はそのままで、重ならないように工夫をします。
また既に描画している場所を記録していないため、文字列を描画する場所に文字列がないかを判断する方法がありません。こちらは2次元配列などで管理したいと思います。

解決するためのイメージ的は、描画した位置を記録し、そこに描画しようとした場合は再度乱数を生成するようにします。
実現方法は、座標分の2次元配列を用意し、描画されている座標の要素をtrueとすることで描画している範囲を管理します。
作成するpngファイルの大きさはwidthheightで管理しているため、この変数を使用し配列を作成します。

 bool[,] png = new bool[height, width]:
        
先ほどのMeasureStringメソッドを使用し、縦横の長さを取得、左上の座標と計算し、2次元配列と照らし合わせます。
 private bool CheckMeasureSize(string str, Font fontData, int x, int y)
 {
    bool b = true;
    var size2 = demoG.MeasureString(str, fontData);
    for (int i = y; i < size2.Height+y; i++)
    {
        for (int w = x; w < size2.Width+x; w++)
        {
            if (png[i, w])
            {
                b = false;
                break;
            }
        }
    }
    return b;
 }
        
MeasureStringメソッドで取得したデータと描画している場所を管理している配列と比較します。x座標の開始位置から終了位置までと、y座標の開始位置から終了位置までをfor文で回し、その範囲にtrue、つまり描画されていることが確認できた時点でfalseを返しその座標では描画できないことを示します。


課題を解決するためのメソッドを作成する

以上で、「文字がはみ出る」「文字が重なる」問題を解決するためのコードは完成しました。なのでこの2つのコードを組み合わせ、今から描画する予定の文字列が描画可能であるかを判断するメソッドを作ります。
作るといっても、2種類のコードを1つにまとめるだけなので意外と簡単です。

 
 private bool CheckMeasureSize(string str, Font fontData, int x, int y)
 {
    bool b = true;
    var size2 = demoG.MeasureString(str, fontData);
    if(height <= size2.Height+y )
    {
        return false;
    }
    else if(width <= size2.Width+x)
    {
        return false;
    }
    for (int i = y; i < size2.Height+y; i++)
    {
        for (int w = x; w < size2.Width+x; w++)
        {
            if (png[i, w])
            {
                b = false;
                break;
            }
        }
    }
    return b;
 }
        
あとは、描画した際に配列に記録するためのメソッドも必要ですので、作成します。
 private void SetMeasureSize(string str, Font fontData, int x, int y)
 {
    var size2 = g.MeasureString(str, fontData);
    for (int i = y; i < size2.Height+y; i++)
    {
        for (int w = x; w < size2.Width+x; w++)
        {
            png[i, w] = true;
        }
    }
 }
        
これら2つのメソッドを前回のプログラムに追加することで、課題を解決した自作のワードクラウドができます!
なお、これらを実装し画像を生成させた画像がこちらです。
文字が重ならなくなるだけで、ここまで綺麗に見えるものなんですね!
さらに描画する文字数を増やした画像が以下です。
より、らしく画像が生成できるようになりました。


まとめ

ということで、今回は自作ワードクラウドの課題を解決しました。

色々と調べたりバグ直したりで時間はかかりましたが、なんとか解決してそれっぽくできました。おそらく私の実現方法よりも良い方法はあると思いますので、「こんなやり方がある」というのを共有いただけると幸いです。
次は、描画する文字を何かの形にしたり、文字色を工夫できるようにしたいですね。
今回はこの辺で、ではまた!

コメント

このブログの人気の投稿

PowerAppsで座席表を作成する

Power AutomateでTeamsのキーワードをトリガーにする

Power Automateで文字列抽出