PowerPointのオートシェイプで好きな関数の曲線を作る C# NetOfficeによる方法


f:id:whoopsidaisies:20140911101713p:plain
sin(x)/xのオートシェイプ

オートシェイプできれいな波形を作りたいときがたまにあるのだけど,C#を使って上の画像みたいにsin(x)/xのオートシェイプをわりときれいに作れたので手順をまとめる.

試した環境はPowerPoint2010とVisual Studio 2013.バージョンが違うと少し変わる可能性はあると思う.(わざわざC#でやらずともVBAマクロでも同じことは出来るけど,VBAは苦手なのでC#でやる.VBAでのやり方も後述します)

出来上がったPowerPointファイルを以下からダウンロードできるようにしておいた.
sinc.pptx - Google ドライブ

NetOffice.PowerPointのインストール

以前「C#でExcelファイルを作成・グラフを挿入する NetOfficeによる方法 - whoopsidaisies's diary」で紹介した,NetOfficeという.NET用のライブラリを使うとPowerPoint等にアクセスして色々出来るのでそれを利用する.

NetOfficeはNuGet経由でインストール可能.プロジェクトを右クリックして「NuGetパッケージの管理」を選択する.オンラインから「NetOffice.PowerPoint」を検索してインストール.

ソースコード

以下にソースコードを載せる.スライドを作ってAddCurveメソッドで曲線を追加しているだけである.

AddCurveメソッドの引数として,float[(点の数),2]に座標を格納して渡す.この点の座標を元にベジェ曲線を描くようである.点の数は3n+1個にしないと止まるので注意.

点の座標はスライドの左上が(0,0)でピクセルで指定する.スライドのオブジェクトのPageSetupプロパティにスライドの幅と高さが格納されているのでそれを元に大きさを調整した.

static void Main(string[] args)
{
    // パワーポイントを開く
    using (var app = new NetOffice.PowerPointApi.Application())
    {
        // プレゼンテーションの作成
        var presentation = app.Presentations.Add();
        // スライドの追加
        var slide = presentation.Slides.Add(1, NetOffice.PowerPointApi.Enums.PpSlideLayout.ppLayoutBlank);

        // sin(x)/xの座標を得る
        var sinc = GetSinc(presentation.PageSetup.SlideWidth, presentation.PageSetup.SlideHeight);

        // 座標からベジェ曲線を描画する
        // 引数はfloatの二次元配列.座標の数は6n+1個じゃないといけないため注意.
        slide.Shapes.AddCurve(sinc);

        // プレゼンテーションの保存
        presentation.SaveAs(@"D:\a.pptx");
        // プレゼンテーションファイルを閉じる
        presentation.Close();
        // パワーポイントの終了 作成したファイル以外も閉じられるので注意
        //app.Quit();
    }
}

/// <summary>
/// sin(x)/x関数の座標をスライドの大きさに合わせて拡大,
/// </summary>
/// <param name="slideWidth">スライドの幅</param>
/// <param name="slideHeight">スライドの高さ</param>
/// <returns>sin(x)/x関数を描くための点の座標</returns>
static float[,] GetSinc(float slideWidth, float slideHeight)
{
    // xが-4π~4πまでのsin(x)/xを求める.601点.
    var xs = Enumerable.Range(-300, 601).Select(x => x * 4 * Math.PI / 300);
    var ys = xs.Select(x => x != 0 ? Math.Sin(x) / x : 1);

    // PowerPointのスライドのサイズに合わせて移動・拡大をする
    // 中心座標
    var cx = slideWidth / 2;
    var cy = slideHeight / 2;
    // オブジェクトのだいたいの大きさ
    var width = slideWidth * 0.8;
    var height = slideHeight * 0.8;
    var pptXs = xs.Select(x => x / (4 * Math.PI) * width / 2 + cx).ToArray();
    var pptYs = ys.Select(y => -y * height / 2 + cy).ToArray();

    // AddCurveメソッドに渡すためにfloatの2次元配列に代入する
    var sinc = new float[pptXs.Length, 2];
    for (int i = 0; i < pptXs.Length; ++ i)
    {
        sinc[i, 0] = (float)pptXs[i];
        sinc[i, 1] = (float)pptYs[i];
    }
    return sinc;
}

実行すると,記事の最初の画像のpptxファイルが作成される.GetSincメソッドの部分を適当な座標を格納をするように変えれば自分の好きな曲線を引ける.

ちなみに出来た曲線はオートシェイプなので,以下の画像のように線の太さとか種類とか色とかも簡単に変えられる.
f:id:whoopsidaisies:20140911115617p:plain
拡大してもきれい.
f:id:whoopsidaisies:20140911115623p:plain

VBAマクロでのやり方

記事の最初にも述べたが,C#を使わずともVBAマクロで同じことが出来る.以下に参考になるコードを載せる(ここのサンプル見やすくしただけだけど).

これの点の座標を引きたい曲線の座標にしてやればOK.

Sub DrawCurve()
    ' 1枚目のスライドのオブジェクトを得る
    Dim slide As PowerPoint.slide
    Set slide = ActivePresentation.Slides(1)
       
    ' ベジェ曲線を引くための点の座標を配列に格納
    ' ここの座標の値を適当に変えてやれば好きな曲線が引ける
    Dim points(1 To 7, 1 To 2) As Single
    points(1, 1) = 0
    points(1, 2) = 0
    points(2, 1) = 72
    points(2, 2) = 72
    points(3, 1) = 100
    points(3, 2) = 40
    points(4, 1) = 20
    points(4, 2) = 50
    points(5, 1) = 90
    points(5, 2) = 120
    points(6, 1) = 60
    points(6, 2) = 30
    points(7, 1) = 150
    points(7, 2) = 90
    
    ' 曲線の追加
    slide.Shapes.AddCurve SafeArrayOfPoints:=points
End Sub