【C#】寸法線の描画

GraphicsPathを使うことで、文字を任意角度で表示することが出来るのを知った元メカ屋な私。

これは寸法線の描画に使えそう!

と思い、寸法線の描画部分をクラスにまとめたものを作成してみました。

 

実行画面

 

寸法線描画のクラス↓

public class Dimension
{
    ///<summary>
    /// 線の色を取得設定します。
    /// </summary>
    public static Color LineColor { get; set; } = Color.Green;

    /// <summary>
    /// 文字の色を取得設定します。
    /// </summary>
    public static Color TextColor { get; set; } = Color.DeepSkyBlue;

    /// <summary>
    /// フォントの名前を取得設定します。
    /// </summary>
    public static FontFamily Family { get; set; } = new FontFamily("Arial");

    /// <summary>
    /// フォントのスタイルを取得設定します。
    /// </summary>
    public static FontStyle Style { get; set; } = FontStyle.Regular;

    /// <summary>
    /// 文字のサイズ(emスクエア)を取得設定します。
    /// </summary>
    public static float EmSize { get; set; } = 18.0f;

    /// <summary>
    /// 文字の書式設定を取得設定します。
    /// </summary>
    public static StringFormat Format { get; set; } = new StringFormat();

    /// <summary>
    /// 寸法値の表示位置の中心からのズレを取得設定します。
    /// </summary>
    public static float TextOffsetX { get; set; } = 0.0f;

    /// <summary>
    /// 寸法値の表示位置の寸法線からの距離を取得設定します。
    /// </summary>
    public static float TextOffsetY { get; set; } = 8.0f;

    /// <summary>
    /// 寸法線の線幅を取得設定します。
    /// </summary>
    public static float LineWidth { get; set; } = 1f;

    /// <summary>
    /// 矢印の大きさを取得設定します。
    /// </summary>
    public static float ArrowSize { get; set; } = 8f;


    /// <summary>
    /// 寸法線の描画
    /// </summary>
    /// <param name="g">描画先のGraphicsオブジェクトを指定します。</param>
    /// <param name="StartPoint">寸法を引き出す位置を指定します。(開始点側)</param>
    /// <param name="EndPoint">寸法を引き出す位置を指定します。(終了点側)</param>
    /// <param name="Offset">指定した点から寸法線を表示するまでの距離を指定します。</param>
    /// <param name="Text">寸法値に表示する文字を指定します。</param>
    public static void DrawDimension(Graphics g, PointF StartPoint, PointF EndPoint, float Offset, string Text)
    {
        // 2点間の中心座標
        PointF center = new PointF((StartPoint.X + EndPoint.X) / 2f, (StartPoint.Y + EndPoint.Y) / 2f);
        // 文字の回転角度
        float thRad = (float)Math.Atan2(EndPoint.Y - StartPoint.Y, EndPoint.X - StartPoint.X);  // ラジアン
        float th = thRad * 180.0f / (float)Math.PI;

        // パスの作成
        var pathText = new System.Drawing.Drawing2D.GraphicsPath();

        //////////////////////////////////////////////////////////////////
        // 寸法値の描画
        pathText.AddString(
                    Text,
                    Family,
                    (int)Style,
                    EmSize,
                    new PointF(0, 0),
                    Format);

        // 文字の領域取得
        RectangleF rect = pathText.GetBounds();

        // アフィン変換行列の計算
        var mat = new System.Drawing.Drawing2D.Matrix();

        // いったん文字を原点へ移動(文字領域の中心下側が基準)
        mat.Translate(-rect.Width / 2f, -rect.Height, System.Drawing.Drawing2D.MatrixOrder.Append);
        // オフセット分の移動(Y方向は逆に移動する)
        mat.Translate(TextOffsetX, -TextOffsetY - Offset, System.Drawing.Drawing2D.MatrixOrder.Append);
        // 寸法線に合わせた回転
        mat.Rotate(th, System.Drawing.Drawing2D.MatrixOrder.Append);
        // 表示位置まで移動
        mat.Translate(center.X, center.Y, System.Drawing.Drawing2D.MatrixOrder.Append);

        // パスをアフィン変換
        pathText.Transform(mat);

        // 寸法値用ブラシの作成
        var brushText = new SolidBrush(TextColor);

        // 描画
        g.FillPath(brushText, pathText);

        //////////////////////////////////////////////////////////////////
        // 寸法補助線の描画

        // 寸法線用ペンの作成
        var penLine = new Pen(LineColor, LineWidth);

        float lineLength = Offset + 5f; // 少し飛び出させる

        // StartPoint側の描画
        var StartPointDst = new PointF(
            StartPoint.X + lineLength * (float)Math.Cos(thRad - Math.PI / 2.0),
            StartPoint.Y + lineLength * (float)Math.Sin(thRad - Math.PI / 2.0)
            );
        g.DrawLine(penLine, StartPoint, StartPointDst);

        // EndPoint側の描画
        var EndPointDst = new PointF(
            EndPoint.X + lineLength * (float)Math.Cos(thRad - Math.PI / 2.0),
            EndPoint.Y + lineLength * (float)Math.Sin(thRad - Math.PI / 2.0)
            );
        g.DrawLine(penLine, EndPoint, EndPointDst);

        //////////////////////////////////////////////////////////////////
        // 寸法線(矢印)の描画
        System.Drawing.Drawing2D.AdjustableArrowCap  arrow
                    = new System.Drawing.Drawing2D.AdjustableArrowCap(ArrowSize, ArrowSize, false);

        penLine.CustomStartCap = arrow;
        penLine.CustomEndCap = arrow;

        var StartPointOffset = new PointF(
            StartPoint.X + Offset * (float)Math.Cos(thRad - Math.PI / 2.0),
            StartPoint.Y + Offset * (float)Math.Sin(thRad - Math.PI / 2.0)
            );

        var EndPointOffset = new PointF(
            EndPoint.X + Offset * (float)Math.Cos(thRad - Math.PI / 2.0),
            EndPoint.Y + Offset * (float)Math.Sin(thRad - Math.PI / 2.0)
            );

        // 矢印の描画
        g.DrawLine(penLine, StartPointOffset, EndPointOffset);
    }
}

使用する側はこんな感じで↓

private void Form1_Paint(object sender, PaintEventArgs e)
{
    var StartPoint = new PointF();
    var EndPoint = new PointF();

    // 四角形の描画
    var p = new Pen(Brushes.Black, 2);
    e.Graphics.DrawRectangle(p, 100, 100, 200, 200);
    e.Graphics.DrawRectangle(p, 100, 100, 400, 300);

    ///////////////////////////////////////////////////////////////
    // 横方向の寸法線の描画
    StartPoint.X = 100; StartPoint.Y = 100;
    EndPoint.X = 300; EndPoint.Y = 100;
    Dimension.DrawDimension(e.Graphics, StartPoint, EndPoint, 20, "200");

    StartPoint.X = 100; StartPoint.Y = 100;
    EndPoint.X = 500; EndPoint.Y = 100;
    Dimension.DrawDimension(e.Graphics, StartPoint, EndPoint, 50, "400");

    ///////////////////////////////////////////////////////////////
    // 縦方向の寸法線の描画
    StartPoint.X = 100; StartPoint.Y = 300;
    EndPoint.X = 100; EndPoint.Y = 100;
    Dimension.DrawDimension(e.Graphics, StartPoint, EndPoint, 20, "200");

    StartPoint.X = 100; StartPoint.Y = 400;
    EndPoint.X = 100; EndPoint.Y = 100;
    Dimension.DrawDimension(e.Graphics, StartPoint, EndPoint, 50, "300");

    ///////////////////////////////////////////////////////////////
    // 斜めの寸法線の描画
    StartPoint.X = 100; StartPoint.Y = 100;
    EndPoint.X = 500; EndPoint.Y = 400;
    Dimension.DrawDimension(e.Graphics, StartPoint, EndPoint, 20, "500");
}

プログラムはこちら↓に置いておきました。

DrawDimensionLine.zip(Visual Studio 2015)

 

機械製図的には寸法線の引き出し方向など、怪しい部分もありそうなので、良い感じになるように修正してみて下さい。

 

画像処理のためのC#へ戻る

【C#】GraphicsPathの領域取得

GraphicsPathを囲む外接四角形の領域はGetBoundsメソッドで取得することができます。

private void Form1_Paint(object sender, PaintEventArgs e)
{
    // パスの作成
    var path = new System.Drawing.Drawing2D.GraphicsPath();

    // 四角形の追加
    path.AddRectangle(new Rectangle(30, 50, 50, 80));
    // 多角形の追加
    path.AddPolygon(
        new Point[]{
            new Point(100, 20),
            new Point(150, 200),
            new Point(60, 70)
        }
        );
    // 円の追加
    path.AddEllipse(150, 30, 50, 50);

    // 描画
    e.Graphics.DrawPath(Pens.Red, path);

    // パスに外接する四角形領域
    RectangleF rect = path.GetBounds();

    // 領域の描画
    e.Graphics.DrawRectangle(Pens.Blue, rect.X, rect.Y, rect.Width, rect.Height);

    ////////////////////////////////////////////////////////////////////////////
    // 文字の追加

    // 文字用にパスの作成
    var pathString = new System.Drawing.Drawing2D.GraphicsPath();
    pathString.AddString(
        "GraphicsPath",
        new FontFamily("Arial"),
        (int)FontStyle.Regular,
        48.0f,
        new Point(20, 250), // 文字の表示位置(左上の座標)
        new StringFormat()
        );

    // パスに外接する四角形領域
    RectangleF rectString = pathString.GetBounds();

    e.Graphics.DrawPath(Pens.Red, pathString);

    // 領域の描画
    e.Graphics.DrawRectangle(Pens.Blue, rectString.X, rectString.Y, rectString.Width, rectString.Height);
    // 文字の表示位置(20, 250)を描画
    e.Graphics.FillEllipse(Brushes.Blue, 15, 245, 10, 10);

}

実行結果

 

GraphicsPathを使うと複数の領域の外接四角形の領域が簡単に取得できるので、パスの領域を描画せずとも、領域の最大/最小の範囲を取得するのに便利です。

 

また、文字の領域も実際に描画している領域を取得できるので、これはこれで便利かも??

 

画像処理のためのC#へ戻る

【C#】GraphicsPathの描画

GraphicsPathの特長の一つでもあるアフィン変換を駆使した描画をしてみたいと思います。

 

パスの描画は、パスをnewしてAddLineなどのメソッドで図形を描画し、DrawPath(輪郭の描画)やFillPath(塗りつぶした描画)で描画を行います。

private void Form1_Paint(object sender, PaintEventArgs e)
{
    var path = new System.Drawing.Drawing2D.GraphicsPath();

    // 線の追加
    path.AddLine(0, 75, 300, 75);
    // 円の追加
    path.AddEllipse(75, 75, 50, 50);
    // 文字の追加
    path.AddString(
        "GraphicsPath",
        new FontFamily(System.Drawing.Text.GenericFontFamilies.Serif),
        (int)FontStyle.Regular,
        48.0f,
        new Point(10, 20),
        new StringFormat()
        );

    // 描画(塗りつぶす)
    e.Graphics.FillPath(Brushes.Red, path);

    ///////////////////////////////////////////////////////////////////
    // アフィン変換行列
    var mat = new System.Drawing.Drawing2D.Matrix();
    // せん断
    mat.Shear(-2, 0, System.Drawing.Drawing2D.MatrixOrder.Append);
    // 拡大
    mat.Scale(1.5f, 2.0f, System.Drawing.Drawing2D.MatrixOrder.Append);
    // 回転
    mat.Rotate(-30, System.Drawing.Drawing2D.MatrixOrder.Append);
    // 平行移動
    mat.Translate(100, 150, System.Drawing.Drawing2D.MatrixOrder.Append);
    ///////////////////////////////////////////////////////////////////

    // パスをアフィン変換
    path.Transform(mat);
    // Penの作成
    var p = new Pen(Brushes.Blue, 3);
    // 描画(輪郭を線幅3で描画)
    e.Graphics.DrawPath(p, path);
}

実行結果

 

実際に実行して気が付いたのですが、線を塗りつぶし(FillPath)で描画すると、線は表示されないようです。

 

一度作成したPathは使いまわして描画できるので、星みたいな多角形をいくつも書くのにも便利かと思います。

 

文字を歪めたり、回転されられるのは、なんか楽しい!

 

画像処理のためのC#へ戻る